diff --git a/.gn b/.gn index a9187a74..853c59a 100644 --- a/.gn +++ b/.gn
@@ -116,6 +116,7 @@ "//clank/native/framework/BUILD.gn", "//components/domain_reliability/BUILD.gn", + "//components/scheduler/scheduler.gni", "//components/webui_generator/generator/wug.gni", "//content/browser/browser.gni", "//content/child/child.gni",
diff --git a/AUTHORS b/AUTHORS index 85609f3..6bfd1724 100644 --- a/AUTHORS +++ b/AUTHORS
@@ -19,6 +19,7 @@ Adenilson Cavalcanti <a.cavalcanti@samsung.com> Aditya Bhargava <heuristicist@gmail.com> Ajay Berwal <ajay.berwal@samsung.com> +Ajay Berwal <a.berwal@samsung.com> Ajith Kumar V <ajith.v@samsung.com> Aku Kotkavuo <a.kotkavuo@partner.samsung.com> Alex Gabriel <minilogo@gmail.com> @@ -127,7 +128,7 @@ Debashish Samantaray <d.samantaray@samsung.com> Deepak Dilip Borade <deepak.db@samsung.com> Deepak Mittal <deepak.m1@samsung.com> -Deepak Singla <deepak.sa@samsung.com> +Deepak Singla <deepak.s@samsung.com> Derek Halman <d.halman@gmail.com> Devlin Cronin <rdevlin.cronin@gmail.com> Diego Ferreiro Val <elfogris@gmail.com> @@ -574,6 +575,7 @@ Kenneth Strickland <ken.strickland@gmail.com> Olli Syrjälä <olli.syrjala@intel.com> Vishal Bhatnagar <vishal.b@samsung.com> +Yunsik Jang <yunsik.jang@lge.com> BlackBerry Limited <*@blackberry.com> Code Aurora Forum <*@codeaurora.org>
diff --git a/BUILD.gn b/BUILD.gn index 10ac76f8..ba6b223a 100644 --- a/BUILD.gn +++ b/BUILD.gn
@@ -697,8 +697,6 @@ group("linux_default_tests") { testonly = true deps = [ - # components_browsertests TODO(GYP) - "//base:base_unittests", # PASSES (*) 2/25/2015 "//cc:cc_unittests", # PASSES 2/25/2015 "//chrome/test:browser_tests", @@ -706,6 +704,7 @@ "//chrome/test:sync_integration_tests", # Crashes for brettw in GN and GYP. "//chrome/test:unit_tests", # PASSES 2/25/2015 "//chrome/test/chromedriver:chromedriver_unittests", # PASSES 2/25/2015 + "//components:components_browsertests", # PASSES 4/17/2015 "//components:components_unittests", # PASSES 2/27/2015 "//content/test:content_browsertests", "//content/test:content_unittests", # PASSES 2/25/2015 @@ -737,6 +736,7 @@ "//third_party/mojo/src/mojo/edk/test:mojo_public_bindings_unittests", # PASSES 2/25/2015 "//third_party/mojo/src/mojo/edk/test:mojo_public_environment_unittests", # PASSES 2/25/2015 "//third_party/mojo/src/mojo/edk/test:mojo_public_system_unittests", # PASSES 2/25/2015 + "//third_party/mojo/src/mojo/edk/test:mojo_public_utility_unittests", "//ui/accessibility:accessibility_unittests", # PASSES 2/25/2015 "//ui/app_list:app_list_unittests", # PASSES 2/25/2015 "//ui/aura:aura_unittests", # PASSES 2/25/2015 @@ -758,4 +758,73 @@ deps += [ "//components/nacl:nacl_loader_unittests" ] # PASSES 3/28/2015 } } +} else if (is_win) { + group("windows_default_tests") { + testonly = true + deps = [ + "//ash:ash_unittests", # FAILS 4/20/2015 + "//base:base_unittests", # PASSES 4/17/2015 in 32-bit, some 64-bit failures. + "//cc:cc_unittests", # PASSES 4/17/2015 + "//chrome_elf:chrome_elf_unittests", # FAILS 4/20/2015 + "//chrome/test:browser_tests", + "//chrome/test:interactive_ui_tests", + "//chrome/test:sync_integration_tests", # Note: need to turn off incremental linking for debug. + "//chrome/test:unit_tests", + "//chrome/test/chromedriver:chromedriver_unittests", # PASSES 4/20/2015 + "//components:components_browsertests", + "//components:components_unittests", # PASSES 4/17/2015 + "//courgette:courgette_unittests", # PASSES 4/20/2015 + "//content/test:content_browsertests", + "//content/test:content_unittests", # PASSES 4/17/2015 + "//crypto:crypto_unittests", # PASSES 4/17/2015 + "//device:device_unittests", # PASSES 4/17/2015 + "//extensions:extensions_browsertests", # PASSES 4/17/2015 + "//extensions:extensions_unittests", # PASSES 4/17/2015 + "//extensions/shell:app_shell_unittests", # Doesn't compile in 64-bit + "//google_apis/gcm:gcm_unit_tests", # PASSES 4/17/2015 + "//google_apis:google_apis_unittests", # PASSES 4/17/2015 + "//gpu:gpu_unittests", # PASSES 4/17/2015 + "//ipc:ipc_tests", # PASSES 4/17/2015 + "//ipc/mojo:ipc_mojo_unittests", # PASSES 4/17/2015 + "//jingle:jingle_unittests", # PASSES 4/17/2015 + "//media/cast:cast_unittests", # PASSES 4/17/2015 + "//media:media_unittests", # PASSES 4/17/2015 + "//mojo/common:mojo_common_unittests", # PASSES 4/17/2015 + "//net:net_unittests", # PASSES 4/17/2015 + "//ppapi:ppapi_unittests", # PASSES 4/17/2015 + "//printing:printing_unittests", # PASSES 4/17/2015 + "//remoting:remoting_unittests", # PASSES 4/17/2015 + "//sandbox/win:sbox_integration_tests", # PASSES 4/20/2015 + "//sandbox/win:sbox_unittests", # PASSES 4/20/2015 + "//sandbox/win:sbox_validation_tests", # PASSES 4/20/2015 + "//skia:skia_unittests", # PASSES 4/17/2015 + "//sql:sql_unittests", # PASSES 4/17/2015 + "//sync:sync_unit_tests", # PASSES 4/20/2015 + "//third_party/cacheinvalidation:cacheinvalidation_unittests", # PASSES 4/20/2015 + "//third_party/mojo/src/mojo/edk/system:mojo_system_unittests", # Seems to hang? + "//third_party/mojo/src/mojo/edk/test:mojo_public_bindings_unittests", # FAILS + "//third_party/mojo/src/mojo/edk/test:mojo_public_environment_unittests", # PASSES 4/20/2015 + "//third_party/mojo/src/mojo/edk/test:mojo_public_system_unittests", # PASSES 4/20/2015 + "//third_party/mojo/src/mojo/edk/test:mojo_public_utility_unittests", # PASSES 4/20/2015 + "//ui/accessibility:accessibility_unittests", # PASSES 4/20/2015 + "//ui/app_list:app_list_unittests", # PASSES 4/20/2015 + "//ui/aura:aura_unittests", # PASSES 4/17/2015 + "//ui/base:ui_base_unittests", # PASSES 4/20/2015 + "//ui/compositor:compositor_unittests", # PASSES 4/20/2015 + "//ui/display:display_unittests", # PASSES 4/20/2015 + "//ui/events:events_unittests", # PASSES 4/20/2015 + "//ui/gfx:gfx_unittests", # PASSES (with assertion failure?) 4/20/2015 + "//ui/message_center:message_center_unittests", # PASSES 4/20/2015 + "//ui/touch_selection:ui_touch_selection_unittests", # PASSES 4/20/2015 + "//ui/views:views_unittests", # Same as WM unittests + "//ui/wm:wm_unittests", # CRASHES, looks like something simple missing. + "//url:url_unittests", # PASSES 4/17/2015 + + # TODO(GYP) installer_util_unittests + # TODO(GYP) app_installer_unittests + # TODO(GYP) nacl_integration + # TODO(GYP) telemetry_perf_unittests + # TODO(GYP) telemetry_unittests + ] + } }
diff --git a/DEPS b/DEPS index 3561fa6..d79a5e914 100644 --- a/DEPS +++ b/DEPS
@@ -34,31 +34,31 @@ 'llvm_url': 'http://src.chromium.org/llvm-project', 'llvm_git': 'https://llvm.googlesource.com', 'webkit_trunk': 'http://src.chromium.org/blink/trunk', - 'webkit_revision': 'fc69639936aee079feee18eb2ca09d2875dad003', # from svn revision 194104 + 'webkit_revision': 'ed1c4f3c9dbd0a07e21a4b8b68e39632f474cb20', # from svn revision 194275 'chromium_git': 'https://chromium.googlesource.com', 'chromiumos_git': 'https://chromium.googlesource.com/chromiumos', 'pdfium_git': 'https://pdfium.googlesource.com', 'skia_git': 'https://skia.googlesource.com', 'boringssl_git': 'https://boringssl.googlesource.com', - 'libvpx_revision': '0816cf21b0f04c1d2cf86b4e3d120a49e4ac8fa6', + 'libvpx_revision': 'c600ca703b712ac0d2db719970a1fce6de70fcb4', 'sfntly_revision': '1bdaae8fc788a5ac8936d68bf24f37d977a13dac', - 'skia_revision': '0e95b116fcc7bec1bbdf5f0459ebd464c458f714', + 'skia_revision': '743be194eda4c1f37c4a5f62f38ef88f09f30649', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling Skia # and V8 without interference from each other. 'v8_branch': 'trunk', - 'v8_revision': '95ffd2a05fa5fe78cf3aeb3e41bcc586518ae518', + 'v8_revision': 'e2ee9248e7c7b2a5a2f919f593290478b5905bb7', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling WebRTC # and V8 without interference from each other. # 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. - 'swarming_revision': '13e7c88b5a9494467259603486f001694ea85721', + 'swarming_revision': 'f222001cc23c7cdb574bf4cfb447f65c94bc6da3', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling ANGLE # and whatever else without interference from each other. - 'angle_revision': '8d95b741c56cc05cfba65d1a98496c5623c6b47f', + 'angle_revision': 'ad0a486b943316caef81084b10fbaa8be70a8956', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling build tools # and whatever else without interference from each other. @@ -66,7 +66,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling PDFium # and whatever else without interference from each other. - 'pdfium_revision': '19ae17578f99621100a26dac3e2c7c3dbf7c7cd1', + 'pdfium_revision': '97d10aff654e42c1b7c3d2abf33fbcf8d341799e', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling openmax_dl # and whatever else without interference from each other. @@ -90,11 +90,11 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling lss # and whatever else without interference from each other. - 'lss_revision': 'e079768b7e3a94dcbe7d338496c0c3bde7151b6e', + 'lss_revision': '6f97298fe3794e92c8c896a6bc06e0b36e4c3de3', # 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': 'b1ee35b27061683b3405e7a67616120c2853067a', + 'nacl_revision': 'efe1c643770e76a1c4747e4c5bf9338e5a0ef945', } # Only these hosts are allowed for dependencies in this DEPS file. @@ -108,7 +108,7 @@ deps = { 'src/breakpad/src': - Var('chromium_git') + '/external/google-breakpad/src.git' + '@' + 'ad88d965aa35dabdfe0cded39f72dde9e4a3591e', # from svn revision 1447 + Var('chromium_git') + '/external/google-breakpad/src.git' + '@' + 'fa5a6b3449dbe77a107a891b96c0e47387302e2e', # from svn revision 1453 'src/buildtools': Var('chromium_git') + '/chromium/buildtools.git' + '@' + Var('buildtools_revision'), @@ -132,7 +132,7 @@ Var('chromium_git') + '/crashpad/crashpad.git' + '@' + '1baff4ff92fe1a1ead6b88b5f01633a4f0b6b51c', 'src/third_party/trace-viewer': - Var('chromium_git') + '/external/trace-viewer.git' + '@' + '337d7911f38529a2a42c5e6c13240076c3fc7d17', + Var('chromium_git') + '/external/trace-viewer.git' + '@' + '98cca163de50a003a33712e1bc0b64a400bd3915', 'src/third_party/WebKit': Var('chromium_git') + '/chromium/blink.git' + '@' + Var('webkit_revision'), @@ -210,7 +210,7 @@ Var('chromium_git') + '/chromium/third_party/ffmpeg.git' + '@' + '104f872faf2cd809cdada885a1e39be85e5b3316', 'src/third_party/libjingle/source/talk': - Var('chromium_git') + '/external/webrtc/trunk/talk.git' + '@' + 'b0e40f3acddbc205818c8f0109095053e1626fe4', # commit position 9029 + Var('chromium_git') + '/external/webrtc/trunk/talk.git' + '@' + '58bf070857c078248d300f2c4bc7daabe0dd43ec', 'src/third_party/usrsctp/usrsctplib': Var('chromium_git') + '/external/usrsctplib.git' + '@' + '36444a999739e9e408f8f587cb4c3ffeef2e50ac', # from svn revision 9215 @@ -234,7 +234,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' + '@' + '647d7226458ac2abc675eeeb2395151e660dee4d', # commit position 9030 + Var('chromium_git') + '/external/webrtc/trunk/webrtc.git' + '@' + '0f07171706abb5174bdf002eee928b4852fc6c22', 'src/third_party/openmax_dl': Var('chromium_git') + '/external/webrtc/deps/third_party/openmax.git' + '@' + Var('openmax_dl_revision'), @@ -263,7 +263,7 @@ Var('chromium_git') + '/chromium/tools/deps2git.git' + '@' + 'f04828eb0b5acd3e7ad983c024870f17f17b06d9', 'src/third_party/webpagereplay': - Var('chromium_git') + '/external/github.com/chromium/web-page-replay.git' + '@' + '13560f74b4f40f12108de7fc43f20b873471d48d', + Var('chromium_git') + '/external/github.com/chromium/web-page-replay.git' + '@' + '4e4d540a18c099e2ad805696072f95815f313c0e', 'src/third_party/pywebsocket/src': Var('chromium_git') + '/external/pywebsocket/src.git' + '@' + 'cb349e87ddb30ff8d1fa1a89be39cec901f4a29c',
diff --git a/PRESUBMIT.py b/PRESUBMIT.py index 99c02609..71360cb 100644 --- a/PRESUBMIT.py +++ b/PRESUBMIT.py
@@ -251,13 +251,16 @@ ), ), ( - 'MessageLoopProxy', + '\<MessageLoopProxy\>', ( 'MessageLoopProxy is deprecated. ', 'Please use SingleThreadTaskRunner or ThreadTaskRunnerHandle instead.' ), True, - (), + ( + # Internal message_loop related code may still use it. + r'^base[\\\/]message_loop[\\\/].*', + ), ), )
diff --git a/WATCHLISTS b/WATCHLISTS index 1aa2750..7f26da13 100644 --- a/WATCHLISTS +++ b/WATCHLISTS
@@ -629,7 +629,7 @@ }, 'scheduler': { 'filepath': 'cc/scheduler'\ - '|content/child/scheduler'\ + '|components/scheduler'\ '|content/renderer/scheduler' }, 'search': {
diff --git a/android_webview/DEPS b/android_webview/DEPS index 2803eaf..319db9e 100644 --- a/android_webview/DEPS +++ b/android_webview/DEPS
@@ -8,6 +8,7 @@ "-android_webview/lib", "+components/data_reduction_proxy", + "+components/devtools_discovery", "+content/public/common", "+crypto", "+gpu",
diff --git a/android_webview/android_webview.gyp b/android_webview/android_webview.gyp index fb4c3fff..9fbaf73 100644 --- a/android_webview/android_webview.gyp +++ b/android_webview/android_webview.gyp
@@ -163,6 +163,7 @@ '../components/components.gyp:cdm_renderer', '../components/components.gyp:crash_component', '../components/components.gyp:data_reduction_proxy_core_browser', + '../components/components.gyp:devtools_discovery', '../components/components.gyp:navigation_interception', '../components/components.gyp:printing_common', '../components/components.gyp:printing_renderer', @@ -206,6 +207,8 @@ 'browser/aw_contents_io_thread_client.h', 'browser/aw_cookie_access_policy.cc', 'browser/aw_cookie_access_policy.h', + 'browser/aw_dev_tools_discovery_provider.cc', + 'browser/aw_dev_tools_discovery_provider.h', 'browser/aw_dev_tools_manager_delegate.cc', 'browser/aw_dev_tools_manager_delegate.h', 'browser/aw_download_manager_delegate.cc',
diff --git a/android_webview/browser/aw_browser_context.cc b/android_webview/browser/aw_browser_context.cc index e108895..1623736 100644 --- a/android_webview/browser/aw_browser_context.cc +++ b/android_webview/browser/aw_browser_context.cc
@@ -12,6 +12,7 @@ #include "android_webview/browser/jni_dependency_factory.h" #include "android_webview/browser/net/aw_url_request_context_getter.h" #include "android_webview/browser/net/init_native_callback.h" +#include "android_webview/common/aw_content_client.h" #include "base/base_paths_android.h" #include "base/bind.h" #include "base/path_service.h" @@ -161,7 +162,8 @@ url_request_context_getter_->GetNetLog(), BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO), BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI), - false /* enable_quic */)); + false /* enable_quic */, + GetUserAgent())); data_reduction_proxy_settings_.reset( new data_reduction_proxy::DataReductionProxySettings()); data_reduction_proxy_service_.reset(
diff --git a/android_webview/browser/aw_browser_main_parts.cc b/android_webview/browser/aw_browser_main_parts.cc index d894e7e..be793ee 100644 --- a/android_webview/browser/aw_browser_main_parts.cc +++ b/android_webview/browser/aw_browser_main_parts.cc
@@ -5,6 +5,7 @@ #include "android_webview/browser/aw_browser_main_parts.h" #include "android_webview/browser/aw_browser_context.h" +#include "android_webview/browser/aw_dev_tools_discovery_provider.h" #include "android_webview/browser/aw_result_codes.h" #include "android_webview/native/public/aw_assets.h" #include "base/android/build_info.h" @@ -12,6 +13,7 @@ #include "base/android/memory_pressure_listener_android.h" #include "base/files/file_path.h" #include "base/path_service.h" +#include "components/devtools_discovery/devtools_discovery_manager.h" #include "content/public/browser/render_process_host.h" #include "content/public/common/content_client.h" #include "content/public/common/content_switches.h" @@ -92,6 +94,12 @@ void AwBrowserMainParts::PreMainMessageLoopRun() { browser_context_->PreMainMessageLoopRun(); + + devtools_discovery::DevToolsDiscoveryManager* discovery_manager = + devtools_discovery::DevToolsDiscoveryManager::GetInstance(); + discovery_manager->AddProvider(make_scoped_ptr( + new AwDevToolsDiscoveryProvider())); + // This is needed for WebView Classic backwards compatibility // See crbug.com/298495 content::SetMaxURLChars(20 * 1024 * 1024);
diff --git a/android_webview/browser/aw_dev_tools_discovery_provider.cc b/android_webview/browser/aw_dev_tools_discovery_provider.cc new file mode 100644 index 0000000..c38189d --- /dev/null +++ b/android_webview/browser/aw_dev_tools_discovery_provider.cc
@@ -0,0 +1,79 @@ +// 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 "android_webview/browser/aw_dev_tools_discovery_provider.h" + +#include "android_webview/browser/browser_view_renderer.h" +#include "base/json/json_writer.h" +#include "base/strings/stringprintf.h" +#include "base/strings/utf_string_conversions.h" +#include "base/values.h" +#include "components/devtools_discovery/basic_target_descriptor.h" +#include "components/devtools_discovery/devtools_discovery_manager.h" +#include "content/public/browser/devtools_agent_host.h" +#include "content/public/browser/web_contents.h" + +using content::DevToolsAgentHost; +using content::WebContents; + +namespace { + +std::string GetViewDescription(WebContents* web_contents) { + android_webview::BrowserViewRenderer* bvr = + android_webview::BrowserViewRenderer::FromWebContents(web_contents); + if (!bvr) return ""; + base::DictionaryValue description; + description.SetBoolean("attached", bvr->attached_to_window()); + description.SetBoolean("visible", bvr->IsVisible()); + gfx::Rect screen_rect = bvr->GetScreenRect(); + description.SetInteger("screenX", screen_rect.x()); + description.SetInteger("screenY", screen_rect.y()); + description.SetBoolean("empty", screen_rect.size().IsEmpty()); + if (!screen_rect.size().IsEmpty()) { + description.SetInteger("width", screen_rect.width()); + description.SetInteger("height", screen_rect.height()); + } + std::string json; + base::JSONWriter::Write(&description, &json); + return json; +} + +class TargetDescriptor : public devtools_discovery::BasicTargetDescriptor { + public: + explicit TargetDescriptor(scoped_refptr<DevToolsAgentHost> agent_host); + + // devtools_discovery::BasicTargetDescriptor overrides. + std::string GetDescription() const override { return description_; } + + private: + std::string description_; +}; + +TargetDescriptor::TargetDescriptor(scoped_refptr<DevToolsAgentHost> agent_host) + : BasicTargetDescriptor(agent_host) { + if (WebContents* web_contents = agent_host->GetWebContents()) + description_ = GetViewDescription(web_contents); +} + +} // namespace + +namespace android_webview { + +AwDevToolsDiscoveryProvider::AwDevToolsDiscoveryProvider() { +} + +AwDevToolsDiscoveryProvider::~AwDevToolsDiscoveryProvider() { +} + +devtools_discovery::DevToolsTargetDescriptor::List +AwDevToolsDiscoveryProvider::GetDescriptors() { + DevToolsAgentHost::List agent_hosts = DevToolsAgentHost::GetOrCreateAll(); + devtools_discovery::DevToolsTargetDescriptor::List result; + result.reserve(agent_hosts.size()); + for (const auto& agent_host : agent_hosts) + result.push_back(new TargetDescriptor(agent_host)); + return result; +} + +} // namespace android_webview
diff --git a/android_webview/browser/aw_dev_tools_discovery_provider.h b/android_webview/browser/aw_dev_tools_discovery_provider.h new file mode 100644 index 0000000..81fdffe --- /dev/null +++ b/android_webview/browser/aw_dev_tools_discovery_provider.h
@@ -0,0 +1,27 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef ANDROID_WEBVIEW_BROWSER_AW_DEV_TOOLS_DISCOVERY_PROVIDER_H_ +#define ANDROID_WEBVIEW_BROWSER_AW_DEV_TOOLS_DISCOVERY_PROVIDER_H_ + +#include "components/devtools_discovery/devtools_discovery_manager.h" + +namespace android_webview { + +class AwDevToolsDiscoveryProvider : + public devtools_discovery::DevToolsDiscoveryManager::Provider { + public: + AwDevToolsDiscoveryProvider(); + ~AwDevToolsDiscoveryProvider() override; + + // devtools_discovery::DevToolsDiscoveryManager::Provider implementation. + devtools_discovery::DevToolsTargetDescriptor::List GetDescriptors() override; + + private: + DISALLOW_COPY_AND_ASSIGN(AwDevToolsDiscoveryProvider); +}; + +} // namespace android_webview + +#endif // ANDROID_WEBVIEW_BROWSER_AW_DEV_TOOLS_DISCOVERY_PROVIDER_H_
diff --git a/android_webview/browser/aw_dev_tools_manager_delegate.cc b/android_webview/browser/aw_dev_tools_manager_delegate.cc index 1b3517d..d46e402 100644 --- a/android_webview/browser/aw_dev_tools_manager_delegate.cc +++ b/android_webview/browser/aw_dev_tools_manager_delegate.cc
@@ -4,94 +4,8 @@ #include "android_webview/browser/aw_dev_tools_manager_delegate.h" -#include "android_webview/browser/browser_view_renderer.h" -#include "base/bind.h" -#include "base/json/json_writer.h" -#include "base/strings/stringprintf.h" -#include "base/strings/utf_string_conversions.h" -#include "base/values.h" -#include "content/public/browser/devtools_agent_host.h" +#include "components/devtools_discovery/devtools_discovery_manager.h" #include "content/public/browser/devtools_target.h" -#include "content/public/browser/web_contents.h" - -using content::DevToolsAgentHost; -using content::RenderViewHost; -using content::WebContents; - -namespace { - -const char kTargetTypePage[] = "page"; -const char kTargetTypeServiceWorker[] = "service_worker"; -const char kTargetTypeOther[] = "other"; - -std::string GetViewDescription(WebContents* web_contents); - -class Target : public content::DevToolsTarget { - public: - explicit Target(scoped_refptr<DevToolsAgentHost> agent_host); - - std::string GetId() const override { return agent_host_->GetId(); } - std::string GetParentId() const override { return std::string(); } - std::string GetType() const override { - switch (agent_host_->GetType()) { - case DevToolsAgentHost::TYPE_WEB_CONTENTS: - return kTargetTypePage; - case DevToolsAgentHost::TYPE_SERVICE_WORKER: - return kTargetTypeServiceWorker; - default: - break; - } - return kTargetTypeOther; - } - std::string GetTitle() const override { return agent_host_->GetTitle(); } - std::string GetDescription() const override { return description_; } - GURL GetURL() const override { return agent_host_->GetURL(); } - GURL GetFaviconURL() const override { return GURL(); } - base::TimeTicks GetLastActivityTime() const override { - return last_activity_time_; - } - bool IsAttached() const override { return agent_host_->IsAttached(); } - scoped_refptr<DevToolsAgentHost> GetAgentHost() const override { - return agent_host_; - } - bool Activate() const override { return agent_host_->Activate(); } - bool Close() const override { return agent_host_->Close(); } - - private: - scoped_refptr<DevToolsAgentHost> agent_host_; - std::string description_; - base::TimeTicks last_activity_time_; -}; - -Target::Target(scoped_refptr<DevToolsAgentHost> agent_host) - : agent_host_(agent_host) { - if (WebContents* web_contents = agent_host->GetWebContents()) { - description_ = GetViewDescription(web_contents); - last_activity_time_ = web_contents->GetLastActiveTime(); - } -} - -std::string GetViewDescription(WebContents* web_contents) { - android_webview::BrowserViewRenderer* bvr = - android_webview::BrowserViewRenderer::FromWebContents(web_contents); - if (!bvr) return ""; - base::DictionaryValue description; - description.SetBoolean("attached", bvr->attached_to_window()); - description.SetBoolean("visible", bvr->IsVisible()); - gfx::Rect screen_rect = bvr->GetScreenRect(); - description.SetInteger("screenX", screen_rect.x()); - description.SetInteger("screenY", screen_rect.y()); - description.SetBoolean("empty", screen_rect.size().IsEmpty()); - if (!screen_rect.size().IsEmpty()) { - description.SetInteger("width", screen_rect.width()); - description.SetInteger("height", screen_rect.height()); - } - std::string json; - base::JSONWriter::Write(&description, &json); - return json; -} - -} // namespace namespace android_webview { @@ -109,11 +23,10 @@ void AwDevToolsManagerDelegate::EnumerateTargets(TargetCallback callback) { TargetList targets; - DevToolsAgentHost::List agents = DevToolsAgentHost::GetOrCreateAll(); - for (DevToolsAgentHost::List::iterator it = agents.begin(); - it != agents.end(); ++it) { - targets.push_back(new Target(*it)); - } + devtools_discovery::DevToolsDiscoveryManager* discovery_manager = + devtools_discovery::DevToolsDiscoveryManager::GetInstance(); + for (const auto& descriptor : discovery_manager->GetDescriptors()) + targets.push_back(descriptor); callback.Run(targets); }
diff --git a/android_webview/browser/aw_dev_tools_manager_delegate.h b/android_webview/browser/aw_dev_tools_manager_delegate.h index 68f22851..52b7c85f 100644 --- a/android_webview/browser/aw_dev_tools_manager_delegate.h +++ b/android_webview/browser/aw_dev_tools_manager_delegate.h
@@ -10,6 +10,8 @@ namespace android_webview { +// TODO(dgozman): remove this class after componentization of devtools_discovery +// is finished. class AwDevToolsManagerDelegate : public content::DevToolsManagerDelegate { public: AwDevToolsManagerDelegate();
diff --git a/android_webview/browser/aw_ssl_host_state_delegate.cc b/android_webview/browser/aw_ssl_host_state_delegate.cc index 59d717fb..1da4747 100644 --- a/android_webview/browser/aw_ssl_host_state_delegate.cc +++ b/android_webview/browser/aw_ssl_host_state_delegate.cc
@@ -86,4 +86,15 @@ : SSLHostStateDelegate::DENIED; } +void AwSSLHostStateDelegate::RevokeUserAllowExceptions( + const std::string& host) { + cert_policy_for_host_.erase(host); +} + +bool AwSSLHostStateDelegate::HasAllowException(const std::string& host) const { + auto policy_iterator = cert_policy_for_host_.find(host); + return policy_iterator != cert_policy_for_host_.end() && + policy_iterator->second.HasAllowException(); +} + } // namespace android_webview
diff --git a/android_webview/browser/aw_ssl_host_state_delegate.h b/android_webview/browser/aw_ssl_host_state_delegate.h index bc3e1a8..b5a34f4a 100644 --- a/android_webview/browser/aw_ssl_host_state_delegate.h +++ b/android_webview/browser/aw_ssl_host_state_delegate.h
@@ -30,6 +30,10 @@ // remember the user's choice. void Allow(const net::X509Certificate& cert, net::CertStatus error); + // Returns true if and only if there exists a user allow exception for some + // certificate. + bool HasAllowException() const { return allowed_.size() > 0; } + private: // The set of fingerprints of allowed certificates. std::map<net::SHA256HashValue, net::CertStatus, net::SHA256HashValueLessThan> @@ -65,6 +69,16 @@ bool DidHostRunInsecureContent(const std::string& host, int pid) const override; + // Revokes all SSL certificate error allow exceptions made by the user for + // |host|. + void RevokeUserAllowExceptions(const std::string& host) override; + + // Returns whether the user has allowed a certificate error exception for + // |host|. This does not mean that *all* certificate errors are allowed, just + // that there exists an exception. To see if a particular certificate and + // error combination exception is allowed, use QueryPolicy(). + bool HasAllowException(const std::string& host) const override; + private: // Certificate policies for each host. std::map<std::string, internal::CertPolicy> cert_policy_for_host_;
diff --git a/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumFactoryProvider.java b/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumFactoryProvider.java index 041ffe2..d608ac9 100644 --- a/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumFactoryProvider.java +++ b/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumFactoryProvider.java
@@ -125,7 +125,7 @@ ThreadUtils.setWillOverrideUiThread(); // Load chromium library. - AwBrowserProcess.loadLibrary(); + AwBrowserProcess.loadLibrary(getWrappedCurrentApplicationContext()); final PackageInfo packageInfo = WebViewFactory.getLoadedPackageInfo();
diff --git a/android_webview/java/src/org/chromium/android_webview/AwBrowserProcess.java b/android_webview/java/src/org/chromium/android_webview/AwBrowserProcess.java index 96d9fb0..c1529e6 100644 --- a/android_webview/java/src/org/chromium/android_webview/AwBrowserProcess.java +++ b/android_webview/java/src/org/chromium/android_webview/AwBrowserProcess.java
@@ -30,8 +30,8 @@ * to run webview in this process. Does not create threads; safe to call from zygote. * Note: it is up to the caller to ensure this is only called once. */ - public static void loadLibrary() { - PathUtils.setPrivateDataDirectorySuffix(PRIVATE_DATA_DIRECTORY_SUFFIX); + public static void loadLibrary(Context context) { + PathUtils.setPrivateDataDirectorySuffix(PRIVATE_DATA_DIRECTORY_SUFFIX, context); try { LibraryLoader libraryLoader = LibraryLoader.get(LibraryProcessType.PROCESS_WEBVIEW); libraryLoader.loadNow();
diff --git a/android_webview/java/src/org/chromium/android_webview/AwContentViewClient.java b/android_webview/java/src/org/chromium/android_webview/AwContentViewClient.java index a397d79..0f4e866a 100644 --- a/android_webview/java/src/org/chromium/android_webview/AwContentViewClient.java +++ b/android_webview/java/src/org/chromium/android_webview/AwContentViewClient.java
@@ -195,11 +195,6 @@ } @Override - public boolean isJavascriptEnabled() { - return mAwSettings != null && mAwSettings.getJavaScriptEnabled(); - } - - @Override public boolean isExternalFlingActive() { return mAwContents.isFlingActive(); }
diff --git a/android_webview/java/src/org/chromium/android_webview/AwContents.java b/android_webview/java/src/org/chromium/android_webview/AwContents.java index 6b061fed..b7603afd 100644 --- a/android_webview/java/src/org/chromium/android_webview/AwContents.java +++ b/android_webview/java/src/org/chromium/android_webview/AwContents.java
@@ -2245,14 +2245,14 @@ * @see android.webkit.WebView#onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo) */ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { - if (!isDestroyed()) mContentViewCore.onInitializeAccessibilityNodeInfo(info); + // TODO(boliu): remove this method. } /** * @see android.webkit.WebView#onInitializeAccessibilityEvent(AccessibilityEvent) */ public void onInitializeAccessibilityEvent(AccessibilityEvent event) { - if (!isDestroyed()) mContentViewCore.onInitializeAccessibilityEvent(event); + // TODO(boliu): remove this method. } public boolean supportsAccessibilityAction(int action) {
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/HttpCacheTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/HttpCacheTest.java index 9a4569e..333e9a6 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/HttpCacheTest.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/HttpCacheTest.java
@@ -4,6 +4,7 @@ package org.chromium.android_webview.test; +import android.content.Context; import android.os.Build; import android.test.suitebuilder.annotation.SmallTest; @@ -71,11 +72,11 @@ @SmallTest @Feature({"AndroidWebView"}) public void testLegacyHttpCacheDirIsRemovedOnStartup() throws Exception { + Context targetContext = getInstrumentation().getTargetContext(); PathUtils.setPrivateDataDirectorySuffix( - AwBrowserProcess.PRIVATE_DATA_DIRECTORY_SUFFIX, - getInstrumentation().getTargetContext()); + AwBrowserProcess.PRIVATE_DATA_DIRECTORY_SUFFIX, targetContext); File webViewLegacyCacheDir = new File( - PathUtils.getDataDirectory(getInstrumentation().getTargetContext()), "Cache"); + PathUtils.getDataDirectory(targetContext), "Cache"); if (!webViewLegacyCacheDir.isDirectory()) { assertTrue(webViewLegacyCacheDir.mkdir()); assertTrue(webViewLegacyCacheDir.isDirectory()); @@ -84,7 +85,7 @@ assertTrue(dummyCacheFile.exists()); // Set up JNI bindings. - AwBrowserProcess.loadLibrary(); + AwBrowserProcess.loadLibrary(targetContext); // No delay before removing the legacy cache files. AwContentsStatics.setLegacyCacheRemovalDelayForTest(0);
diff --git a/android_webview/test/shell/src/org/chromium/android_webview/shell/AwShellApplication.java b/android_webview/test/shell/src/org/chromium/android_webview/shell/AwShellApplication.java index 638d1d9..5e1a0701 100644 --- a/android_webview/test/shell/src/org/chromium/android_webview/shell/AwShellApplication.java +++ b/android_webview/test/shell/src/org/chromium/android_webview/shell/AwShellApplication.java
@@ -39,7 +39,7 @@ Log.e(TAG, "Java debugger connected. Resuming execution."); } - AwBrowserProcess.loadLibrary(); + AwBrowserProcess.loadLibrary(this); if (CommandLine.getInstance().hasSwitch(AwShellSwitches.ENABLE_ATRACE)) { Log.e(TAG, "Enabling Android trace.");
diff --git a/android_webview/test/shell/src/org/chromium/android_webview/test/AwTestContainerView.java b/android_webview/test/shell/src/org/chromium/android_webview/test/AwTestContainerView.java index c95a4eaf..1373a2b9 100644 --- a/android_webview/test/shell/src/org/chromium/android_webview/test/AwTestContainerView.java +++ b/android_webview/test/shell/src/org/chromium/android_webview/test/AwTestContainerView.java
@@ -15,8 +15,6 @@ import android.view.MotionEvent; import android.view.SurfaceHolder; import android.view.View; -import android.view.accessibility.AccessibilityEvent; -import android.view.accessibility.AccessibilityNodeInfo; import android.view.accessibility.AccessibilityNodeProvider; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputConnection; @@ -412,20 +410,6 @@ } @Override - public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { - super.onInitializeAccessibilityNodeInfo(info); - info.setClassName(AwContents.class.getName()); - mAwContents.onInitializeAccessibilityNodeInfo(info); - } - - @Override - public void onInitializeAccessibilityEvent(AccessibilityEvent event) { - super.onInitializeAccessibilityEvent(event); - event.setClassName(AwContents.class.getName()); - mAwContents.onInitializeAccessibilityEvent(event); - } - - @Override public boolean performAccessibilityAction(int action, Bundle arguments) { return mAwContents.performAccessibilityAction(action, arguments); }
diff --git a/ash/ash_chromeos_strings.grdp b/ash/ash_chromeos_strings.grdp index f14825d3..ab48212f 100644 --- a/ash/ash_chromeos_strings.grdp +++ b/ash/ash_chromeos_strings.grdp
@@ -337,11 +337,11 @@ <message name="IDS_ASH_STATUS_TRAY_NOTIFICATION_SESSION_LENGTH_LIMIT" desc="Notification shown to inform the user that the session length is limited."> Session ends in <ph name="session_time_remaining">$1<ex>4 minutes 23 seconds</ex></ph>. You will be signed out. </message> - <message name="IDS_ASH_STATUS_TRAY_ROTATION_LOCK_AUTO" desc="The text shown in the tray menu when rotation is set to auto."> - Rotation on (Tap here to change) + <message name="IDS_ASH_STATUS_TRAY_ROTATION_LOCK_AUTO" desc="The text shown in the tray menu when rotation is set to auto and the user can enable the rotation lock by tapping."> + Enable rotation lock </message> - <message name="IDS_ASH_STATUS_TRAY_ROTATION_LOCK_LOCKED" desc="The text shown in the tray menu when rotation is set to locked."> - Rotation locked (Tap here to change) + <message name="IDS_ASH_STATUS_TRAY_ROTATION_LOCK_LOCKED" desc="The text shown in the tray menu when rotation is set to locked and tapping will disable the lock."> + Disable rotation lock </message> <message name="IDS_ASH_STATUS_TRAY_KEYBOARD_DISABLED" desc="The text shown in the tray menu when the virtual keyboard is disabled."> On-screen keyboard disabled @@ -412,4 +412,10 @@ <message name="IDS_ASH_LOGOUT_CONFIRMATION_BUTTON" desc="The text for okay button of the logout confirmation dialog."> Sign out now </message> + <message name="IDS_ASH_STATUS_TRAY_NEW_LOGIN_UI_ENABLED" desc="The text shown in the tray menu when the WebView-based sign-in is enabled."> + New login UI is ON + </message> + <message name="IDS_ASH_STATUS_TRAY_NEW_LOGIN_UI_DISABLED" desc="The text shown in the tray menu when the WebView-based sign-in is disabled."> + New login UI is OFF + </message> </grit-part>
diff --git a/ash/ash_switches.h b/ash/ash_switches.h index 0623d3b1..40aba02 100644 --- a/ash/ash_switches.h +++ b/ash/ash_switches.h
@@ -47,15 +47,6 @@ ASH_EXPORT extern const char kForceAshToDesktop[]; #endif -// Returns true if items can be dragged off the shelf to unpin. -ASH_EXPORT bool UseDragOffShelf(); - -#if defined(OS_CHROMEOS) -// Returns true if a notification should appear when a low-power USB charger -// is connected. -ASH_EXPORT bool UseUsbChargerNotification(); -#endif - } // namespace switches } // namespace ash
diff --git a/ash/content/display/screen_orientation_controller_chromeos.cc b/ash/content/display/screen_orientation_controller_chromeos.cc index dbd4465..ac15726 100644 --- a/ash/content/display/screen_orientation_controller_chromeos.cc +++ b/ash/content/display/screen_orientation_controller_chromeos.cc
@@ -37,13 +37,12 @@ const float kMinimumAccelerationScreenRotation = 4.2f; blink::WebScreenOrientationLockType GetDisplayNaturalOrientation() { - ash::DisplayManager* display_manager = - ash::Shell::GetInstance()->display_manager(); - if (!display_manager->HasInternalDisplay()) + if (!gfx::Display::HasInternalDisplay()) return blink::WebScreenOrientationLockLandscape; ash::DisplayInfo info = - display_manager->GetDisplayInfo(gfx::Display::InternalDisplayId()); + ash::Shell::GetInstance()->display_manager()->GetDisplayInfo( + gfx::Display::InternalDisplayId()); gfx::Size size = info.size_in_pixel(); switch (info.rotation()) { case gfx::Display::ROTATE_0: @@ -102,19 +101,17 @@ rotation_locked_orientation_ = blink::WebScreenOrientationLockAny; FOR_EACH_OBSERVER(Observer, observers_, OnRotationLockChanged(rotation_locked_)); - DisplayManager* display_manager = Shell::GetInstance()->display_manager(); - if (!display_manager->HasInternalDisplay()) + if (!gfx::Display::HasInternalDisplay()) return; base::AutoReset<bool> auto_ignore_display_configuration_updates( &ignore_display_configuration_updates_, true); - display_manager->RegisterDisplayRotationProperties(rotation_locked_, - current_rotation_); + Shell::GetInstance()->display_manager()->RegisterDisplayRotationProperties( + rotation_locked_, current_rotation_); } void ScreenOrientationController::SetDisplayRotation( gfx::Display::Rotation rotation) { - DisplayManager* display_manager = Shell::GetInstance()->display_manager(); - if (!display_manager->HasInternalDisplay()) + if (!gfx::Display::HasInternalDisplay()) return; current_rotation_ = rotation; base::AutoReset<bool> auto_ignore_display_configuration_updates( @@ -196,9 +193,9 @@ void ScreenOrientationController::OnDisplayConfigurationChanged() { if (ignore_display_configuration_updates_) return; - DisplayManager* display_manager = Shell::GetInstance()->display_manager(); - if (!display_manager->HasInternalDisplay()) + if (!gfx::Display::HasInternalDisplay()) return; + DisplayManager* display_manager = Shell::GetInstance()->display_manager(); gfx::Display::Rotation user_rotation = display_manager->GetDisplayInfo(gfx::Display::InternalDisplayId()) .rotation(); @@ -212,13 +209,14 @@ } void ScreenOrientationController::OnMaximizeModeStarted() { - DisplayManager* display_manager = Shell::GetInstance()->display_manager(); // Do not exit early, as the internal display can be determined after Maximize // Mode has started. (chrome-os-partner:38796) // Always start observing. - if (display_manager->HasInternalDisplay()) { + if (gfx::Display::HasInternalDisplay()) { current_rotation_ = user_rotation_ = - display_manager->GetDisplayInfo(gfx::Display::InternalDisplayId()) + Shell::GetInstance() + ->display_manager() + ->GetDisplayInfo(gfx::Display::InternalDisplayId()) .rotation(); } if (!rotation_locked_) @@ -294,10 +292,10 @@ void ScreenOrientationController::LockToRotationMatchingOrientation( blink::WebScreenOrientationLockType lock_orientation) { - DisplayManager* display_manager = Shell::GetInstance()->display_manager(); - if (!display_manager->HasInternalDisplay()) + if (!gfx::Display::HasInternalDisplay()) return; + DisplayManager* display_manager = Shell::GetInstance()->display_manager(); gfx::Display::Rotation rotation = display_manager->GetDisplayInfo(gfx::Display::InternalDisplayId()) .rotation();
diff --git a/ash/display/display_controller.cc b/ash/display/display_controller.cc index 166a76f..0068e95c 100644 --- a/ash/display/display_controller.cc +++ b/ash/display/display_controller.cc
@@ -340,7 +340,6 @@ RootWindowController::CreateForSecondaryDisplay(ash_host); } } - UpdateHostWindowNames(); FOR_EACH_OBSERVER(Observer, observers_, OnDisplaysInitialized()); } @@ -436,10 +435,9 @@ Shell* shell = Shell::GetInstance(); DisplayConfiguratorAnimation* animation = shell->display_configurator_animation(); - animation->StartFadeOutAnimation( - base::Bind(&DisplayController::SetMirrorModeAfterAnimation, - weak_ptr_factory_.GetWeakPtr(), - !display_manager->IsMirrored())); + animation->StartFadeOutAnimation(base::Bind( + &DisplayController::SetMirrorModeAfterAnimation, + weak_ptr_factory_.GetWeakPtr(), !display_manager->IsInMirrorMode())); #endif } @@ -481,10 +479,10 @@ const gfx::Display& new_primary_display) { DisplayManager* display_manager = GetDisplayManager(); DCHECK(new_primary_display.is_valid()); - DCHECK(display_manager->IsActiveDisplay(new_primary_display)); + DCHECK(display_manager->GetDisplayForId(new_primary_display.id()).is_valid()); if (!new_primary_display.is_valid() || - !display_manager->IsActiveDisplay(new_primary_display)) { + !display_manager->GetDisplayForId(new_primary_display.id()).is_valid()) { LOG(ERROR) << "Invalid or non-existent display is requested:" << new_primary_display.ToString(); return; @@ -781,7 +779,7 @@ DisplayLayoutStore* layout_store = display_manager->layout_store(); if (display_manager->num_connected_displays() > 1) { DisplayIdPair pair = display_manager->GetCurrentDisplayIdPair(); - layout_store->UpdateMirrorStatus(pair, display_manager->IsMirrored()); + layout_store->UpdateMirrorStatus(pair, display_manager->IsInMirrorMode()); DisplayLayout layout = layout_store->GetRegisteredDisplayLayout(pair); if (Shell::GetScreen()->GetNumDisplays() > 1 ) { @@ -798,7 +796,6 @@ } } FOR_EACH_OBSERVER(Observer, observers_, OnDisplayConfigurationChanged()); - UpdateHostWindowNames(); UpdateMouseLocationAfterDisplayChange(); } @@ -847,21 +844,4 @@ GetDisplayManager()->SetMirrorMode(mirror); } -void DisplayController::UpdateHostWindowNames() { -#if defined(USE_X11) - // crbug.com/120229 - set the window title for the primary dislpay - // to "aura_root_0" so gtalk can find the primary root window to broadcast. - // TODO(jhorwich) Remove this once Chrome supports window-based broadcasting. - aura::Window* primary = Shell::GetPrimaryRootWindow(); - aura::Window::Windows root_windows = Shell::GetAllRootWindows(); - for (size_t i = 0; i < root_windows.size(); ++i) { - std::string name = - root_windows[i] == primary ? "aura_root_0" : "aura_root_x"; - gfx::AcceleratedWidget xwindow = - root_windows[i]->GetHost()->GetAcceleratedWidget(); - XStoreName(gfx::GetXDisplay(), xwindow, name.c_str()); - } -#endif -} - } // namespace ash
diff --git a/ash/display/display_controller.h b/ash/display/display_controller.h index e795d77..3dc8189 100644 --- a/ash/display/display_controller.h +++ b/ash/display/display_controller.h
@@ -174,8 +174,6 @@ void SetMirrorModeAfterAnimation(bool mirror); - void UpdateHostWindowNames(); - class DisplayChangeLimiter { public: DisplayChangeLimiter();
diff --git a/ash/display/display_controller_unittest.cc b/ash/display/display_controller_unittest.cc index 6945fac2..6b9e056 100644 --- a/ash/display/display_controller_unittest.cc +++ b/ash/display/display_controller_unittest.cc
@@ -36,12 +36,6 @@ #include "ui/wm/public/activation_change_observer.h" #include "ui/wm/public/activation_client.h" -#if defined(USE_X11) -#include <X11/Xlib.h> -#include "ui/gfx/x/x11_types.h" -#undef RootWindow -#endif - namespace ash { namespace { @@ -361,23 +355,6 @@ GetEffectiveUIScale(); } -#if defined(USE_X11) -void GetPrimaryAndSeconary(aura::Window** primary, - aura::Window** secondary) { - *primary = Shell::GetPrimaryRootWindow(); - aura::Window::Windows root_windows = Shell::GetAllRootWindows(); - *secondary = root_windows[0] == *primary ? root_windows[1] : root_windows[0]; -} - -std::string GetXWindowName(aura::WindowTreeHost* host) { - char* name = NULL; - XFetchName(gfx::GetXDisplay(), host->GetAcceleratedWidget(), &name); - std::string ret(name); - XFree(name); - return ret; -} -#endif - class TestMouseWatcherListener : public views::MouseWatcherListener { public: TestMouseWatcherListener() {} @@ -1307,32 +1284,6 @@ EXPECT_EQ(20, Shell::GetScreen()->GetPrimaryDisplay().id()); } -#if defined(USE_X11) -TEST_F(DisplayControllerTest, XWidowNameForRootWindow) { - EXPECT_EQ("aura_root_0", GetXWindowName( - Shell::GetPrimaryRootWindow()->GetHost())); - - // Multiple display. - UpdateDisplay("200x200,300x300"); - aura::Window* primary, *secondary; - GetPrimaryAndSeconary(&primary, &secondary); - EXPECT_EQ("aura_root_0", GetXWindowName(primary->GetHost())); - EXPECT_EQ("aura_root_x", GetXWindowName(secondary->GetHost())); - - // Swap primary. - primary = secondary = NULL; - Shell::GetInstance()->display_controller()->SwapPrimaryDisplay(); - GetPrimaryAndSeconary(&primary, &secondary); - EXPECT_EQ("aura_root_0", GetXWindowName(primary->GetHost())); - EXPECT_EQ("aura_root_x", GetXWindowName(secondary->GetHost())); - - // Switching back to single display. - UpdateDisplay("300x400"); - EXPECT_EQ("aura_root_0", GetXWindowName( - Shell::GetPrimaryRootWindow()->GetHost())); -} -#endif - TEST_F(DisplayControllerTest, UpdateMouseLocationAfterDisplayChange) { if (!SupportsMultipleDisplays()) return;
diff --git a/ash/display/display_manager.cc b/ash/display/display_manager.cc index b731ded..d40917ac 100644 --- a/ash/display/display_manager.cc +++ b/ash/display/display_manager.cc
@@ -20,6 +20,7 @@ #include "base/command_line.h" #include "base/logging.h" #include "base/metrics/histogram.h" +#include "base/run_loop.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_split.h" #include "base/strings/stringprintf.h" @@ -103,6 +104,10 @@ } } +bool IsInternalDisplayId(int64 id) { + return gfx::Display::InternalDisplayId() == id; +} + } // namespace using std::string; @@ -110,19 +115,17 @@ DisplayManager::DisplayManager() : delegate_(NULL), - screen_ash_(new ScreenAsh), - screen_(screen_ash_.get()), + screen_(new ScreenAsh), layout_store_(new DisplayLayoutStore), first_display_id_(gfx::Display::kInvalidDisplayID), num_connected_displays_(0), force_bounds_changed_(false), change_display_upon_host_resize_(false), second_display_mode_(EXTENDED), - mirrored_display_id_(gfx::Display::kInvalidDisplayID), + mirroring_display_id_(gfx::Display::kInvalidDisplayID), registered_internal_display_rotation_lock_(false), registered_internal_display_rotation_(gfx::Display::ROTATE_0), weak_ptr_factory_(this) { - #if defined(OS_CHROMEOS) // Enable only on the device so that DisplayManagerFontTest passes. if (base::SysInfo::IsRunningOnChromeOS()) @@ -130,16 +133,14 @@ change_display_upon_host_resize_ = !base::SysInfo::IsRunningOnChromeOS(); #endif - gfx::Screen::SetScreenInstance(gfx::SCREEN_TYPE_ALTERNATE, - screen_ash_.get()); + gfx::Screen::SetScreenInstance(gfx::SCREEN_TYPE_ALTERNATE, screen_.get()); gfx::Screen* current_native = gfx::Screen::GetScreenByType(gfx::SCREEN_TYPE_NATIVE); // If there is no native, or the native was for shutdown, // use ash's screen. if (!current_native || current_native == screen_for_shutdown) { - gfx::Screen::SetScreenInstance(gfx::SCREEN_TYPE_NATIVE, - screen_ash_.get()); + gfx::Screen::SetScreenInstance(gfx::SCREEN_TYPE_NATIVE, screen_.get()); } } @@ -187,7 +188,7 @@ // internal display may have bigger scale factor in case the external display // is an 4K display. float largest_device_scale_factor = 1.0f; - for (const gfx::Display& display : displays_) { + for (const gfx::Display& display : active_display_list_) { const ash::DisplayInfo& info = display_info_[display.id()]; largest_device_scale_factor = std::max( largest_device_scale_factor, info.GetEffectiveDeviceScaleFactor()); @@ -196,23 +197,6 @@ #endif // OS_CHROMEOS } -bool DisplayManager::IsActiveDisplay(const gfx::Display& display) const { - for (DisplayList::const_iterator iter = displays_.begin(); - iter != displays_.end(); ++iter) { - if ((*iter).id() == display.id()) - return true; - } - return false; -} - -bool DisplayManager::HasInternalDisplay() const { - return gfx::Display::InternalDisplayId() != gfx::Display::kInvalidDisplayID; -} - -bool DisplayManager::IsInternalDisplayId(int64 id) const { - return gfx::Display::InternalDisplayId() == id; -} - DisplayLayout DisplayManager::GetCurrentDisplayLayout() { DCHECK_LE(2U, num_connected_displays()); // Invert if the primary was swapped. @@ -228,27 +212,27 @@ // On release build, just fallback to default instead of blowing up. DisplayLayout layout = layout_store_->default_display_layout(); - layout.primary_id = displays_[0].id(); + layout.primary_id = active_display_list_[0].id(); return layout; } DisplayIdPair DisplayManager::GetCurrentDisplayIdPair() const { - if (IsMirrored()) { + if (IsInMirrorMode()) { if (software_mirroring_enabled()) { CHECK_EQ(2u, num_connected_displays()); // This comment is to make it easy to distinguish the crash // between two checks. - CHECK_EQ(1u, displays_.size()); + CHECK_EQ(1u, active_display_list_.size()); } - return std::make_pair(displays_[0].id(), mirrored_display_id_); + return std::make_pair(active_display_list_[0].id(), mirroring_display_id_); } else { - CHECK_LE(2u, displays_.size()); - int64 id_at_zero = displays_[0].id(); + CHECK_LE(2u, active_display_list_.size()); + int64 id_at_zero = active_display_list_[0].id(); if (id_at_zero == gfx::Display::InternalDisplayId() || id_at_zero == first_display_id()) { - return std::make_pair(id_at_zero, displays_[1].id()); + return std::make_pair(id_at_zero, active_display_list_[1].id()); } else { - return std::make_pair(displays_[1].id(), id_at_zero); + return std::make_pair(active_display_list_[1].id(), id_at_zero); } } } @@ -281,10 +265,10 @@ // Primary's bounds stay the same. Just notify bounds change // on the secondary. - screen_ash_->NotifyMetricsChanged( + screen_->NotifyMetricsChanged( ScreenUtil::GetSecondaryDisplay(), gfx::DisplayObserver::DISPLAY_METRIC_BOUNDS | - gfx::DisplayObserver::DISPLAY_METRIC_WORK_AREA); + gfx::DisplayObserver::DISPLAY_METRIC_WORK_AREA); if (delegate_) delegate_->PostDisplayConfigurationChange(); } @@ -298,13 +282,12 @@ const gfx::Display& DisplayManager::FindDisplayContainingPoint( const gfx::Point& point_in_screen) const { - for (DisplayList::const_iterator iter = displays_.begin(); - iter != displays_.end(); ++iter) { - const gfx::Display& display = *iter; - if (display.bounds().Contains(point_in_screen)) - return display; - } - return GetInvalidDisplay(); + auto iter = + std::find_if(active_display_list_.begin(), active_display_list_.end(), + [point_in_screen](const gfx::Display& display) { + return display.bounds().Contains(point_in_screen); + }); + return iter != active_display_list_.end() ? *iter : GetInvalidDisplay(); } bool DisplayManager::UpdateWorkAreaOfDisplay(int64 display_id, @@ -320,9 +303,8 @@ const gfx::Insets& insets_in_dip) { bool update = false; DisplayInfoList display_info_list; - for (DisplayList::const_iterator iter = displays_.begin(); - iter != displays_.end(); ++iter) { - DisplayInfo info = GetDisplayInfo(iter->id()); + for (const auto& display : active_display_list_) { + DisplayInfo info = GetDisplayInfo(display.id()); if (info.id() == display_id) { if (insets_in_dip.empty()) { info.set_clear_overscan_insets(true); @@ -345,9 +327,8 @@ void DisplayManager::SetDisplayRotation(int64 display_id, gfx::Display::Rotation rotation) { DisplayInfoList display_info_list; - for (DisplayList::const_iterator iter = displays_.begin(); - iter != displays_.end(); ++iter) { - DisplayInfo info = GetDisplayInfo(iter->id()); + for (const auto& display : active_display_list_) { + DisplayInfo info = GetDisplayInfo(display.id()); if (info.id() == display_id) { if (info.rotation() == rotation) return; @@ -368,9 +349,8 @@ bool found = false; // TODO(mukai): merge this implementation into SetDisplayMode(). DisplayInfoList display_info_list; - for (DisplayList::const_iterator iter = displays_.begin(); - iter != displays_.end(); ++iter) { - DisplayInfo info = GetDisplayInfo(iter->id()); + for (const auto& display : active_display_list_) { + DisplayInfo info = GetDisplayInfo(display.id()); if (info.id() == display_id) { found = true; if (info.configured_ui_scale() == ui_scale) @@ -423,9 +403,8 @@ DisplayInfoList display_info_list; bool display_property_changed = false; bool resolution_changed = false; - for (DisplayList::const_iterator iter = displays_.begin(); - iter != displays_.end(); ++iter) { - DisplayInfo info = GetDisplayInfo(iter->id()); + for (const auto& display : active_display_list_) { + DisplayInfo info = GetDisplayInfo(display.id()); if (info.id() == display_id) { const std::vector<DisplayMode>& modes = info.display_modes(); std::vector<DisplayMode>::const_iterator iter = @@ -571,11 +550,11 @@ const std::vector<DisplayInfo>& updated_displays) { if (updated_displays.empty()) { VLOG(1) << "OnNativeDisplaysChanged(0): # of current displays=" - << displays_.size(); + << active_display_list_.size(); // If the device is booted without display, or chrome is started // without --ash-host-window-bounds on linux desktop, use the // default display. - if (displays_.empty()) { + if (active_display_list_.empty()) { std::vector<DisplayInfo> init_displays; init_displays.push_back(DisplayInfo::CreateFromSpec(std::string())); MaybeInitInternalDisplay(&init_displays[0]); @@ -607,8 +586,8 @@ bool internal_display_connected = false; num_connected_displays_ = updated_displays.size(); - mirrored_display_id_ = gfx::Display::kInvalidDisplayID; - mirroring_display_ = gfx::Display(); + mirroring_display_id_ = gfx::Display::kInvalidDisplayID; + software_mirroring_display_ = gfx::Display(); DisplayInfoList new_display_info_list; for (DisplayInfoList::const_iterator iter = updated_displays.begin(); iter != updated_displays.end(); @@ -619,7 +598,7 @@ gfx::Point origin = iter->bounds_in_native().origin(); if (origins.find(origin) != origins.end()) { InsertAndUpdateDisplayInfo(*iter); - mirrored_display_id_ = iter->id(); + mirroring_display_id_ = iter->id(); } else { origins.insert(origin); new_display_info_list.push_back(*iter); @@ -643,10 +622,9 @@ else if (display_modes_.find(iter->id()) != display_modes_.end()) display_modes_[iter->id()] = *display_modes_iter; } - if (HasInternalDisplay() && - !internal_display_connected && + if (gfx::Display::HasInternalDisplay() && !internal_display_connected && display_info_.find(gfx::Display::InternalDisplayId()) == - display_info_.end()) { + display_info_.end()) { DisplayInfo internal_display_info( gfx::Display::InternalDisplayId(), l10n_util::GetStringUTF8(IDS_ASH_INTERNAL_DISPLAY_NAME), @@ -659,10 +637,8 @@ void DisplayManager::UpdateDisplays() { DisplayInfoList display_info_list; - for (DisplayList::const_iterator iter = displays_.begin(); - iter != displays_.end(); ++iter) { - display_info_list.push_back(GetDisplayInfo(iter->id())); - } + for (const auto& display : active_display_list_) + display_info_list.push_back(GetDisplayInfo(display.id())); AddMirrorDisplayInfoIfAny(&display_info_list); UpdateDisplays(display_info_list); } @@ -676,55 +652,30 @@ #endif DisplayInfoList new_display_info_list = updated_display_info_list; - std::sort(displays_.begin(), displays_.end(), DisplaySortFunctor()); + std::sort(active_display_list_.begin(), active_display_list_.end(), + DisplaySortFunctor()); std::sort(new_display_info_list.begin(), new_display_info_list.end(), DisplayInfoSortFunctor()); + // Close the mirroring window if any here to avoid creating two compositor on + // one display. + if (delegate_) + delegate_->CloseMirroringDisplay(); + + if (second_display_mode_ == MIRRORING && new_display_info_list.size() == 2) + CreateSoftwareMirroringDisplay(&new_display_info_list); + + DisplayList new_displays; DisplayList removed_displays; std::map<size_t, uint32_t> display_changes; std::vector<size_t> added_display_indices; - DisplayList::iterator curr_iter = displays_.begin(); + DisplayList::iterator curr_iter = active_display_list_.begin(); DisplayInfoList::const_iterator new_info_iter = new_display_info_list.begin(); - DisplayList new_displays; - - // Use the internal display or 1st as the mirror source, then scale - // the root window so that it matches the external display's - // resolution. This is necessary in order for scaling to work while - // mirrored. - int64 mirroing_display_id = gfx::Display::kInvalidDisplayID; - - if (second_display_mode_ != EXTENDED && new_display_info_list.size() == 2) { - bool zero_is_source = - first_display_id_ == new_display_info_list[0].id() || - gfx::Display::InternalDisplayId() == new_display_info_list[0].id(); - DCHECK_EQ(MIRRORING, second_display_mode_); - mirrored_display_id_ = new_display_info_list[zero_is_source ? 1 : 0].id(); - mirroing_display_id = mirrored_display_id_; - } - - while (curr_iter != displays_.end() || + while (curr_iter != active_display_list_.end() || new_info_iter != new_display_info_list.end()) { - if (new_info_iter != new_display_info_list.end() && - mirroing_display_id == new_info_iter->id()) { - DisplayInfo info = *new_info_iter; - info.SetOverscanInsets(gfx::Insets()); - InsertAndUpdateDisplayInfo(info); - mirroring_display_ = - CreateDisplayFromDisplayInfoById(mirroing_display_id); - ++new_info_iter; - // Remove existing external display if it is going to be used as - // mirroring display. - if (curr_iter != displays_.end() && - curr_iter->id() == mirroing_display_id) { - removed_displays.push_back(*curr_iter); - ++curr_iter; - } - continue; - } - - if (curr_iter == displays_.end()) { + if (curr_iter == active_display_list_.end()) { // more displays in new list. added_display_indices.push_back(new_displays.size()); InsertAndUpdateDisplayInfo(*new_info_iter); @@ -794,7 +745,7 @@ } gfx::Display old_primary; if (delegate_) - old_primary = screen_ash_->GetPrimaryDisplay(); + old_primary = screen_->GetPrimaryDisplay(); // Clear focus if the display has been removed, but don't clear focus if // the destkop has been moved from one display to another @@ -805,29 +756,6 @@ if (delegate_) delegate_->PreDisplayConfigurationChange(clear_focus); - // Do not update |displays_| if there's nothing to be updated. Without this, - // it will not update the display layout, which causes the bug - // http://crbug.com/155948. - if (display_changes.empty() && added_display_indices.empty() && - removed_displays.empty()) { - // When changing from software mirroring mode to sinlge display - // mode, it is possible there is no need to update |displays_| and - // we early out here. But we still need to update the mirroring - // window and call the PostDisplayConfigurationChange() cause - // there are some clients need to act on this, - // e.g. TouchTransformerController needs to adjust the - // TouchTransformer when/ switching from dual displays to single - // display. - if (delegate_) { - if (HasSoftwareMirroringDisplay()) - CreateMirrorWindowAsyncIfAny(); - else - delegate_->CloseMirroringDisplay(); - delegate_->PostDisplayConfigurationChange(); - } - return; - } - std::vector<size_t> updated_indices; if (UpdateNonPrimaryDisplayBoundsForLayout(&new_displays, &updated_indices)) { for (std::vector<size_t>::iterator it = updated_indices.begin(); @@ -846,31 +774,25 @@ } } - displays_ = new_displays; + active_display_list_ = new_displays; RefreshFontParams(); base::AutoReset<bool> resetter(&change_display_upon_host_resize_, false); // Temporarily add displays to be removed because display object // being removed are accessed during shutting down the root. - displays_.insert(displays_.end(), removed_displays.begin(), - removed_displays.end()); + active_display_list_.insert(active_display_list_.end(), + removed_displays.begin(), removed_displays.end()); for (DisplayList::const_reverse_iterator iter = removed_displays.rbegin(); iter != removed_displays.rend(); ++iter) { - screen_ash_->NotifyDisplayRemoved(displays_.back()); - displays_.pop_back(); + screen_->NotifyDisplayRemoved(active_display_list_.back()); + active_display_list_.pop_back(); } - bool has_mirroring_display = HasSoftwareMirroringDisplay(); - // Close the mirroring window here to avoid creating two compositor on - // one display. - if (!has_mirroring_display && delegate_) - delegate_->CloseMirroringDisplay(); - for (std::vector<size_t>::iterator iter = added_display_indices.begin(); iter != added_display_indices.end(); ++iter) { - screen_ash_->NotifyDisplayAdded(displays_[*iter]); + screen_->NotifyDisplayAdded(active_display_list_[*iter]); } bool notify_primary_change = @@ -880,14 +802,14 @@ iter != display_changes.end(); ++iter) { uint32_t metrics = iter->second; - const gfx::Display& updated_display = displays_[iter->first]; + const gfx::Display& updated_display = active_display_list_[iter->first]; if (notify_primary_change && updated_display.id() == screen_->GetPrimaryDisplay().id()) { metrics |= gfx::DisplayObserver::DISPLAY_METRIC_PRIMARY; notify_primary_change = false; } - screen_ash_->NotifyMetricsChanged(updated_display, metrics); + screen_->NotifyMetricsChanged(updated_display, metrics); } if (notify_primary_change) { @@ -903,7 +825,7 @@ if (primary.device_scale_factor() != old_primary.device_scale_factor()) metrics |= gfx::DisplayObserver::DISPLAY_METRIC_DEVICE_SCALE_FACTOR; - screen_ash_->NotifyMetricsChanged(primary, metrics); + screen_->NotifyMetricsChanged(primary, metrics); } } @@ -922,24 +844,24 @@ } const gfx::Display& DisplayManager::GetDisplayAt(size_t index) const { - DCHECK_LT(index, displays_.size()); - return displays_[index]; + DCHECK_LT(index, active_display_list_.size()); + return active_display_list_[index]; } const gfx::Display& DisplayManager::GetPrimaryDisplayCandidate() const { if (GetNumDisplays() != 2) - return displays_[0]; + return active_display_list_[0]; DisplayLayout layout = layout_store_->GetRegisteredDisplayLayout( GetCurrentDisplayIdPair()); return GetDisplayForId(layout.primary_id); } size_t DisplayManager::GetNumDisplays() const { - return displays_.size(); + return active_display_list_.size(); } -bool DisplayManager::IsMirrored() const { - return mirrored_display_id_ != gfx::Display::kInvalidDisplayID; +bool DisplayManager::IsInMirrorMode() const { + return mirroring_display_id_ != gfx::Display::kInvalidDisplayID; } const DisplayInfo& DisplayManager::GetDisplayInfo(int64 display_id) const { @@ -1003,9 +925,10 @@ } void DisplayManager::AddRemoveDisplay() { - DCHECK(!displays_.empty()); + DCHECK(!active_display_list_.empty()); std::vector<DisplayInfo> new_display_info_list; - const DisplayInfo& first_display = GetDisplayInfo(displays_[0].id()); + const DisplayInfo& first_display = + GetDisplayInfo(active_display_list_[0].id()); new_display_info_list.push_back(first_display); // Add if there is only one display connected. if (num_connected_displays() == 1) { @@ -1016,16 +939,16 @@ "%d+%d-500x400", host_bounds.x(), host_bounds.bottom()))); } num_connected_displays_ = new_display_info_list.size(); - mirrored_display_id_ = gfx::Display::kInvalidDisplayID; - mirroring_display_ = gfx::Display(); + mirroring_display_id_ = gfx::Display::kInvalidDisplayID; + software_mirroring_display_ = gfx::Display(); UpdateDisplays(new_display_info_list); } void DisplayManager::ToggleDisplayScaleFactor() { - DCHECK(!displays_.empty()); + DCHECK(!active_display_list_.empty()); std::vector<DisplayInfo> new_display_info_list; - for (DisplayList::const_iterator iter = displays_.begin(); - iter != displays_.end(); ++iter) { + for (DisplayList::const_iterator iter = active_display_list_.begin(); + iter != active_display_list_.end(); ++iter) { DisplayInfo display_info = GetDisplayInfo(iter->id()); display_info.set_device_scale_factor( display_info.device_scale_factor() == 1.0f ? 2.0f : 1.0f); @@ -1047,8 +970,8 @@ void DisplayManager::SetSecondDisplayMode(SecondDisplayMode mode) { second_display_mode_ = mode; - mirrored_display_id_ = gfx::Display::kInvalidDisplayID; - mirroring_display_ = gfx::Display(); + mirroring_display_id_ = gfx::Display::kInvalidDisplayID; + software_mirroring_display_ = gfx::Display(); } bool DisplayManager::UpdateDisplayBounds(int64 display_id, @@ -1056,12 +979,12 @@ if (change_display_upon_host_resize_) { display_info_[display_id].SetBounds(new_bounds); // Don't notify observers if the mirrored window has changed. - if (software_mirroring_enabled() && mirrored_display_id_ == display_id) + if (software_mirroring_enabled() && mirroring_display_id_ == display_id) return false; gfx::Display* display = FindDisplayForId(display_id); display->SetSize(display_info_[display_id].size_in_pixel()); - screen_ash_->NotifyMetricsChanged( - *display, gfx::DisplayObserver::DISPLAY_METRIC_BOUNDS); + screen_->NotifyMetricsChanged(*display, + gfx::DisplayObserver::DISPLAY_METRIC_BOUNDS); return true; } return false; @@ -1071,7 +994,7 @@ // Do not post a task if the software mirroring doesn't exist, or // during initialization when compositor's init task isn't posted yet. // ash::Shell::Init() will call this after the compositor is initialized. - if (!HasSoftwareMirroringDisplay() || !delegate_) + if (!software_mirroring_display_.is_valid() || !delegate_) return; base::MessageLoopForUI::current()->PostTask( FROM_HERE, @@ -1081,10 +1004,9 @@ void DisplayManager::CreateScreenForShutdown() const { bool native_is_ash = - gfx::Screen::GetScreenByType(gfx::SCREEN_TYPE_NATIVE) == - screen_ash_.get(); + gfx::Screen::GetScreenByType(gfx::SCREEN_TYPE_NATIVE) == screen_.get(); delete screen_for_shutdown; - screen_for_shutdown = screen_ash_->CloneForShutdown(); + screen_for_shutdown = screen_->CloneForShutdown(); gfx::Screen::SetScreenInstance(gfx::SCREEN_TYPE_ALTERNATE, screen_for_shutdown); if (native_is_ash) { @@ -1100,20 +1022,45 @@ SetInternalDisplayModeList(info); } +void DisplayManager::CreateSoftwareMirroringDisplay( + DisplayInfoList* display_info_list) { + // Use the internal display or 1st as the mirror source, then scale + // the root window so that it matches the external display's + // resolution. This is necessary in order for scaling to work while + // mirrored. + bool zero_is_source = + first_display_id_ == (*display_info_list)[0].id() || + gfx::Display::InternalDisplayId() == (*display_info_list)[0].id(); + DCHECK_EQ(MIRRORING, second_display_mode_); + mirroring_display_id_ = (*display_info_list)[zero_is_source ? 1 : 0].id(); + int64 display_id = mirroring_display_id_; + auto iter = std::find_if(display_info_list->begin(), display_info_list->end(), + [display_id](const DisplayInfo& info) { + return info.id() == display_id; + }); + DCHECK(iter != display_info_list->end()); + DisplayInfo info = *iter; + info.SetOverscanInsets(gfx::Insets()); + InsertAndUpdateDisplayInfo(info); + software_mirroring_display_ = + CreateDisplayFromDisplayInfoById(mirroring_display_id_); + display_info_list->erase(iter); +} + gfx::Display* DisplayManager::FindDisplayForId(int64 id) { - for (DisplayList::iterator iter = displays_.begin(); - iter != displays_.end(); ++iter) { - if ((*iter).id() == id) - return &(*iter); - } + auto iter = std::find_if( + active_display_list_.begin(), active_display_list_.end(), + [id](const gfx::Display& display) { return display.id() == id; }); + if (iter != active_display_list_.end()) + return &(*iter); DLOG(WARNING) << "Could not find display:" << id; return NULL; } void DisplayManager::AddMirrorDisplayInfoIfAny( std::vector<DisplayInfo>* display_info_list) { - if (software_mirroring_enabled() && IsMirrored()) - display_info_list->push_back(GetDisplayInfo(mirrored_display_id_)); + if (software_mirroring_enabled() && IsInMirrorMode()) + display_info_list->push_back(GetDisplayInfo(mirroring_display_id_)); } void DisplayManager::InsertAndUpdateDisplayInfo(const DisplayInfo& new_info) { @@ -1214,17 +1161,12 @@ } void DisplayManager::CreateMirrorWindowIfAny() { - if (HasSoftwareMirroringDisplay() && delegate_) { - DisplayInfo display_info = GetDisplayInfo(mirroring_display_.id()); + if (software_mirroring_display_.is_valid() && delegate_) { + DisplayInfo display_info = GetDisplayInfo(software_mirroring_display_.id()); delegate_->CreateOrUpdateMirroringDisplay(display_info); } } -bool DisplayManager::HasSoftwareMirroringDisplay() { - return second_display_mode_ != DisplayManager::EXTENDED && - mirroring_display_.is_valid(); -} - // static void DisplayManager::UpdateDisplayBoundsForLayout( const DisplayLayout& layout, @@ -1272,4 +1214,9 @@ secondary_display->UpdateWorkAreaFromInsets(insets); } +void DisplayManager::RunPendingTasksForTest() { + if (software_mirroring_display_.is_valid()) + base::RunLoop().RunUntilIdle(); +} + } // namespace ash
diff --git a/ash/display/display_manager.h b/ash/display/display_manager.h index 092d2b0..9aa686c 100644 --- a/ash/display/display_manager.h +++ b/ash/display/display_manager.h
@@ -68,6 +68,8 @@ virtual void PostDisplayConfigurationChange() = 0; }; + typedef std::vector<gfx::Display> DisplayList; + // How the second display will be used. // 1) EXTENDED mode extends the desktop to the second dislpay. // 2) MIRRORING mode copies the content of the primary display to @@ -88,13 +90,9 @@ return layout_store_.get(); } - gfx::Screen* screen() { - return screen_; - } - void set_delegate(Delegate* delegate) { delegate_ = delegate; } - // When set to true, the MonitorManager calls OnDisplayMetricsChanged + // When set to true, the DisplayManager calls OnDisplayMetricsChanged // even if the display's bounds didn't change. Used to swap primary // display. void set_force_bounds_changed(bool force_bounds_changed) { @@ -115,14 +113,6 @@ // configuration. void RefreshFontParams(); - // True if the given |display| is currently connected. - bool IsActiveDisplay(const gfx::Display& display) const; - - // True if there is an internal display. - bool HasInternalDisplay() const; - - bool IsInternalDisplayId(int64 id) const; - // Returns the display layout used for current displays. DisplayLayout GetCurrentDisplayLayout(); @@ -237,19 +227,21 @@ // when displays are mirrored. size_t GetNumDisplays() const; - const std::vector<gfx::Display>& displays() const { return displays_; } + const DisplayList& active_display_list() const { + return active_display_list_; + } // Returns the number of connected displays. This returns 2 // when displays are mirrored. size_t num_connected_displays() const { return num_connected_displays_; } // Returns the mirroring status. - bool IsMirrored() const; - int64 mirrored_display_id() const { return mirrored_display_id_; } + bool IsInMirrorMode() const; + int64 mirroring_display_id() const { return mirroring_display_id_; } // Returns the display used for software mirrroring. - const gfx::Display& mirroring_display() const { - return mirroring_display_; + const gfx::Display& software_mirroring_display() const { + return software_mirroring_display_; } // Retuns the display info associated with |display_id|. @@ -315,17 +307,21 @@ friend class test::DisplayManagerTestApi; friend class test::SystemGestureEventFilterTest; - typedef std::vector<gfx::Display> DisplayList; + typedef std::vector<DisplayInfo> DisplayInfoList; void set_change_display_upon_host_resize(bool value) { change_display_upon_host_resize_ = value; } + // Creates software mirroring display related information. The display + // used to mirror the content is removed from the |display_info_list|. + void CreateSoftwareMirroringDisplay(DisplayInfoList* display_info_list); + gfx::Display* FindDisplayForId(int64 id); // Add the mirror display's display info if the software based // mirroring is in use. - void AddMirrorDisplayInfoIfAny(std::vector<DisplayInfo>* display_info_list); + void AddMirrorDisplayInfoIfAny(DisplayInfoList* display_info_list); // Inserts and update the DisplayInfo according to the overscan // state. Note that The DisplayInfo stored in the |internal_display_info_| @@ -352,7 +348,7 @@ void CreateMirrorWindowIfAny(); - bool HasSoftwareMirroringDisplay(); + void RunPendingTasksForTest(); static void UpdateDisplayBoundsForLayout( const DisplayLayout& layout, @@ -361,16 +357,14 @@ Delegate* delegate_; // not owned. - scoped_ptr<ScreenAsh> screen_ash_; - // This is to have an accessor without ScreenAsh definition. - gfx::Screen* screen_; + scoped_ptr<ScreenAsh> screen_; scoped_ptr<DisplayLayoutStore> layout_store_; int64 first_display_id_; // List of current active displays. - DisplayList displays_; + DisplayList active_display_list_; int num_connected_displays_; @@ -390,8 +384,8 @@ bool change_display_upon_host_resize_; SecondDisplayMode second_display_mode_; - int64 mirrored_display_id_; - gfx::Display mirroring_display_; + int64 mirroring_display_id_; + gfx::Display software_mirroring_display_; // User preference for rotation lock of the internal display. bool registered_internal_display_rotation_lock_;
diff --git a/ash/display/display_manager_unittest.cc b/ash/display/display_manager_unittest.cc index 4fc4057..9ed1039 100644 --- a/ash/display/display_manager_unittest.cc +++ b/ash/display/display_manager_unittest.cc
@@ -487,7 +487,7 @@ CreateDisplayInfo(internal_display_id, gfx::Rect(0, 0, 500, 500)); const DisplayInfo external_display_info = CreateDisplayInfo(external_id, gfx::Rect(1, 1, 100, 100)); - const DisplayInfo mirrored_display_info = + const DisplayInfo mirroring_display_info = CreateDisplayInfo(mirror_id, gfx::Rect(0, 0, 500, 500)); EXPECT_EQ(1U, display_manager()->GetNumDisplays()); @@ -502,7 +502,7 @@ EXPECT_EQ(default_bounds, display_manager()->GetDisplayAt(0).bounds().ToString()); EXPECT_EQ(1U, display_manager()->num_connected_displays()); - EXPECT_FALSE(display_manager()->IsMirrored()); + EXPECT_FALSE(display_manager()->IsInMirrorMode()); if (!SupportsMultipleDisplays()) return; @@ -516,7 +516,7 @@ EXPECT_EQ("1,1 100x100", GetDisplayInfoForId(external_id).bounds_in_native().ToString()); EXPECT_EQ(1U, display_manager()->num_connected_displays()); - EXPECT_FALSE(display_manager()->IsMirrored()); + EXPECT_FALSE(display_manager()->IsInMirrorMode()); EXPECT_EQ(external_id, Shell::GetScreen()->GetPrimaryDisplay().id()); EXPECT_EQ(internal_display_id, gfx::Display::InternalDisplayId()); @@ -535,7 +535,7 @@ EXPECT_EQ("1,1 100x100", GetDisplayInfoForId(10).bounds_in_native().ToString()); EXPECT_EQ(2U, display_manager()->num_connected_displays()); - EXPECT_FALSE(display_manager()->IsMirrored()); + EXPECT_FALSE(display_manager()->IsInMirrorMode()); EXPECT_EQ(ToDisplayName(internal_display_id), display_manager()->GetDisplayNameForId(internal_display_id)); @@ -548,7 +548,7 @@ EXPECT_EQ("1,1 100x100", GetDisplayInfoForId(10).bounds_in_native().ToString()); EXPECT_EQ(2U, display_manager()->num_connected_displays()); - EXPECT_FALSE(display_manager()->IsMirrored()); + EXPECT_FALSE(display_manager()->IsInMirrorMode()); EXPECT_EQ(ToDisplayName(internal_display_id), display_manager()->GetDisplayNameForId(internal_display_id)); @@ -559,21 +559,21 @@ EXPECT_EQ("0,0 500x500", GetDisplayForId(internal_display_id).bounds().ToString()); EXPECT_EQ(1U, display_manager()->num_connected_displays()); - EXPECT_FALSE(display_manager()->IsMirrored()); + EXPECT_FALSE(display_manager()->IsInMirrorMode()); // External display was changed during suspend. display_info_list.push_back(external_display_info); display_manager()->OnNativeDisplaysChanged(display_info_list); EXPECT_EQ(2U, display_manager()->GetNumDisplays()); EXPECT_EQ(2U, display_manager()->num_connected_displays()); - EXPECT_FALSE(display_manager()->IsMirrored()); + EXPECT_FALSE(display_manager()->IsInMirrorMode()); // suspend... display_info_list.clear(); display_manager()->OnNativeDisplaysChanged(display_info_list); EXPECT_EQ(2U, display_manager()->GetNumDisplays()); EXPECT_EQ(2U, display_manager()->num_connected_displays()); - EXPECT_FALSE(display_manager()->IsMirrored()); + EXPECT_FALSE(display_manager()->IsInMirrorMode()); // and resume with different external display. display_info_list.push_back(internal_display_info); @@ -581,20 +581,20 @@ display_manager()->OnNativeDisplaysChanged(display_info_list); EXPECT_EQ(2U, display_manager()->GetNumDisplays()); EXPECT_EQ(2U, display_manager()->num_connected_displays()); - EXPECT_FALSE(display_manager()->IsMirrored()); - EXPECT_FALSE(display_manager()->IsMirrored()); + EXPECT_FALSE(display_manager()->IsInMirrorMode()); + EXPECT_FALSE(display_manager()->IsInMirrorMode()); // mirrored... display_info_list.clear(); display_info_list.push_back(internal_display_info); - display_info_list.push_back(mirrored_display_info); + display_info_list.push_back(mirroring_display_info); display_manager()->OnNativeDisplaysChanged(display_info_list); EXPECT_EQ(1U, display_manager()->GetNumDisplays()); EXPECT_EQ("0,0 500x500", GetDisplayForId(internal_display_id).bounds().ToString()); EXPECT_EQ(2U, display_manager()->num_connected_displays()); - EXPECT_EQ(11U, display_manager()->mirrored_display_id()); - EXPECT_TRUE(display_manager()->IsMirrored()); + EXPECT_EQ(11U, display_manager()->mirroring_display_id()); + EXPECT_TRUE(display_manager()->IsInMirrorMode()); // Test display name. EXPECT_EQ(ToDisplayName(internal_display_id), @@ -612,7 +612,7 @@ display_manager()->OnNativeDisplaysChanged(display_info_list); EXPECT_EQ(2U, display_manager()->GetNumDisplays()); EXPECT_EQ(2U, display_manager()->num_connected_displays()); - EXPECT_FALSE(display_manager()->IsMirrored()); + EXPECT_FALSE(display_manager()->IsInMirrorMode()); EXPECT_EQ("0,0 500x500", GetDisplayForId(internal_display_id).bounds().ToString()); EXPECT_EQ("500,0 100x100", @@ -627,7 +627,7 @@ EXPECT_EQ("1,1 100x100", GetDisplayInfoForId(external_id).bounds_in_native().ToString()); EXPECT_EQ(1U, display_manager()->num_connected_displays()); - EXPECT_FALSE(display_manager()->IsMirrored()); + EXPECT_FALSE(display_manager()->IsInMirrorMode()); // Switched to another display display_info_list.clear(); @@ -638,7 +638,7 @@ "0,0 500x500", GetDisplayInfoForId(internal_display_id).bounds_in_native().ToString()); EXPECT_EQ(1U, display_manager()->num_connected_displays()); - EXPECT_FALSE(display_manager()->IsMirrored()); + EXPECT_FALSE(display_manager()->IsInMirrorMode()); } // Make sure crash does not happen if add and remove happens at the same time. @@ -1217,13 +1217,13 @@ EXPECT_EQ("400x500", test_api.GetHost()->GetBounds().size().ToString()); EXPECT_EQ("300x400", test_api.GetHost()->window()->bounds().size().ToString()); - EXPECT_TRUE(display_manager->IsMirrored()); + EXPECT_TRUE(display_manager->IsInMirrorMode()); display_manager->SetMirrorMode(false); EXPECT_TRUE(display_observer.changed_and_reset()); EXPECT_EQ(NULL, test_api.GetHost()); EXPECT_EQ(2U, display_manager->GetNumDisplays()); - EXPECT_FALSE(display_manager->IsMirrored()); + EXPECT_FALSE(display_manager->IsInMirrorMode()); // Make sure the mirror window has the pixel size of the // source display. @@ -1268,14 +1268,14 @@ display_manager->SetSecondDisplayMode(DisplayManager::MIRRORING); UpdateDisplay("600x400,600x400"); - EXPECT_TRUE(display_manager->IsMirrored()); + EXPECT_TRUE(display_manager->IsInMirrorMode()); EXPECT_EQ(1U, display_manager->GetNumDisplays()); DisplayController* display_controller = ash::Shell::GetInstance()->display_controller(); EXPECT_TRUE(display_controller->mirror_window_controller()->GetWindow()); UpdateDisplay("600x400"); - EXPECT_FALSE(display_manager->IsMirrored()); + EXPECT_FALSE(display_manager->IsInMirrorMode()); EXPECT_EQ(1U, display_manager->GetNumDisplays()); EXPECT_FALSE(display_controller->mirror_window_controller()->GetWindow()); }
diff --git a/ash/display/mirror_window_controller.cc b/ash/display/mirror_window_controller.cc index 1a08296..c7e5acf0 100644 --- a/ash/display/mirror_window_controller.cc +++ b/ash/display/mirror_window_controller.cc
@@ -125,7 +125,7 @@ DisplayManager* display_manager = Shell::GetInstance()->display_manager(); const DisplayInfo& source_display_info = display_manager->GetDisplayInfo( Shell::GetScreen()->GetPrimaryDisplay().id()); - DCHECK(display_manager->IsMirrored()); + DCHECK(display_manager->IsInMirrorMode()); scoped_ptr<RootWindowTransformer> transformer( CreateRootWindowTransformerForMirroredDisplay(source_display_info, display_info)); @@ -136,7 +136,7 @@ if (ash_host_.get()) { DisplayManager* display_manager = Shell::GetInstance()->display_manager(); const DisplayInfo& mirror_display_info = display_manager->GetDisplayInfo( - display_manager->mirrored_display_id()); + display_manager->mirroring_display_id()); UpdateWindow(mirror_display_info); } } @@ -175,11 +175,11 @@ scoped_ptr<RootWindowTransformer> MirrorWindowController::CreateRootWindowTransformer() const { DisplayManager* display_manager = Shell::GetInstance()->display_manager(); - const DisplayInfo& mirror_display_info = display_manager->GetDisplayInfo( - display_manager->mirrored_display_id()); + const DisplayInfo& mirror_display_info = + display_manager->GetDisplayInfo(display_manager->mirroring_display_id()); const DisplayInfo& source_display_info = display_manager->GetDisplayInfo( Shell::GetScreen()->GetPrimaryDisplay().id()); - DCHECK(display_manager->IsMirrored()); + DCHECK(display_manager->IsInMirrorMode()); return scoped_ptr<RootWindowTransformer>( CreateRootWindowTransformerForMirroredDisplay(source_display_info, mirror_display_info));
diff --git a/ash/display/mirror_window_controller_unittest.cc b/ash/display/mirror_window_controller_unittest.cc index 15bdefaf..c1fe0cb 100644 --- a/ash/display/mirror_window_controller_unittest.cc +++ b/ash/display/mirror_window_controller_unittest.cc
@@ -283,8 +283,8 @@ EXPECT_EQ(internal_id, internal_display_id); EXPECT_EQ(1U, display_manager->GetNumDisplays()); - EXPECT_TRUE(display_manager->IsMirrored()); - EXPECT_EQ(external_id, display_manager->mirrored_display_id()); + EXPECT_TRUE(display_manager->IsInMirrorMode()); + EXPECT_EQ(external_id, display_manager->mirroring_display_id()); // dock mode. display_info_list.clear(); @@ -292,7 +292,7 @@ display_manager->SetSecondDisplayMode(DisplayManager::MIRRORING); display_manager->OnNativeDisplaysChanged(display_info_list); EXPECT_EQ(1U, display_manager->GetNumDisplays()); - EXPECT_FALSE(display_manager->IsMirrored()); + EXPECT_FALSE(display_manager->IsInMirrorMode()); // back to software mirroring. display_info_list.clear(); @@ -301,13 +301,13 @@ display_manager->SetSecondDisplayMode(DisplayManager::MIRRORING); display_manager->OnNativeDisplaysChanged(display_info_list); EXPECT_EQ(1U, display_manager->GetNumDisplays()); - EXPECT_TRUE(display_manager->IsMirrored()); - EXPECT_EQ(external_id, display_manager->mirrored_display_id()); + EXPECT_TRUE(display_manager->IsInMirrorMode()); + EXPECT_EQ(external_id, display_manager->mirroring_display_id()); } TEST_F(MirrorOnBootTest, MAYBE_MirrorOnBoot) { DisplayManager* display_manager = Shell::GetInstance()->display_manager(); - EXPECT_TRUE(display_manager->IsMirrored()); + EXPECT_TRUE(display_manager->IsInMirrorMode()); RunAllPendingInMessageLoop(); test::MirrorWindowTestApi test_api; EXPECT_TRUE(test_api.GetHost());
diff --git a/ash/display/mouse_cursor_event_filter.cc b/ash/display/mouse_cursor_event_filter.cc index c736901..e904881 100644 --- a/ash/display/mouse_cursor_event_filter.cc +++ b/ash/display/mouse_cursor_event_filter.cc
@@ -109,9 +109,8 @@ } // namespace MouseCursorEventFilter::MouseCursorEventFilter() - : mouse_warp_mode_(WARP_ALWAYS), + : mouse_warp_enabled_(true), drag_source_root_(NULL), - scale_when_drag_started_(1.0f), shared_display_edge_indicator_(new SharedDisplayEdgeIndicator) { Shell::GetInstance()->display_controller()->AddObserver(this); } @@ -174,14 +173,6 @@ } void MouseCursorEventFilter::OnMouseEvent(ui::MouseEvent* event) { - aura::Window* target = static_cast<aura::Window*>(event->target()); - - if (event->type() == ui::ET_MOUSE_PRESSED) { - scale_when_drag_started_ = ui::GetDeviceScaleFactor(target->layer()); - } else if (event->type() == ui::ET_MOUSE_RELEASED) { - scale_when_drag_started_ = 1.0f; - } - // Handle both MOVED and DRAGGED events here because when the mouse pointer // enters the other root window while dragging, the underlying window system // (at least X11) stops generating a ui::ET_MOUSE_MOVED event. @@ -246,8 +237,7 @@ bool MouseCursorEventFilter::WarpMouseCursorInNativeCoords( const gfx::Point& point_in_native, const gfx::Point& point_in_screen) { - if (Shell::GetScreen()->GetNumDisplays() <= 1 || - mouse_warp_mode_ == WARP_NONE) + if (Shell::GetScreen()->GetNumDisplays() <= 1 || !mouse_warp_enabled_) return false; bool in_src_edge = src_edge_bounds_in_native_.Contains(point_in_native);
diff --git a/ash/display/mouse_cursor_event_filter.h b/ash/display/mouse_cursor_event_filter.h index 62c02cb6..44496cf 100644 --- a/ash/display/mouse_cursor_event_filter.h +++ b/ash/display/mouse_cursor_event_filter.h
@@ -26,20 +26,10 @@ class ASH_EXPORT MouseCursorEventFilter : public ui::EventHandler, public DisplayController::Observer { public: - enum MouseWarpMode { - WARP_ALWAYS, // Always warp the mouse when possible. - WARP_DRAG, // Used when dragging a window. Top and bottom - // corner of the shared edge is reserved for window - // snapping. - WARP_NONE, // No mouse warping. Used when resizing the window. - }; - MouseCursorEventFilter(); ~MouseCursorEventFilter() override; - void set_mouse_warp_mode(MouseWarpMode mouse_warp_mode) { - mouse_warp_mode_ = mouse_warp_mode; - } + void set_mouse_warp_enabled(bool enabled) { mouse_warp_enabled_ = enabled; } // Shows/Hide the indicator for window dragging. The |from| // is the window where the dragging started. @@ -88,18 +78,17 @@ void UpdateHorizontalEdgeBounds(); void UpdateVerticalEdgeBounds(); - // Returns the source and destination window. When the - // mouse_warp_mode_ is WARP_DRAG, src_window is the root window - // where the drag starts. When the mouse_warp_mode_ is WARP_ALWAYS, - // the src_window is always the primary root window, because there - // is no difference between moving src to dst and moving dst to src. + // Returns the source and destination window. When |src_window| is + // |drag_soruce_root_| when it is set. Otherwise, the |src_window| + // is always the primary root window, because there is no difference + // between moving src to dst and moving dst to src. void GetSrcAndDstRootWindows(aura::Window** src_window, aura::Window** dst_window); bool WarpMouseCursorIfNecessaryForTest(aura::Window* target_root, const gfx::Point& point_in_screen); - MouseWarpMode mouse_warp_mode_; + bool mouse_warp_enabled_; // The bounds for warp hole windows. |dst_indicator_bounds_| is kept // in the instance for testing. @@ -112,8 +101,6 @@ // The root window in which the dragging started. aura::Window* drag_source_root_; - float scale_when_drag_started_; - // Shows the area where a window can be dragged in to/out from // another display. scoped_ptr<SharedDisplayEdgeIndicator> shared_display_edge_indicator_;
diff --git a/ash/display/mouse_cursor_event_filter_unittest.cc b/ash/display/mouse_cursor_event_filter_unittest.cc index d1b9f38..58a1a0f 100644 --- a/ash/display/mouse_cursor_event_filter_unittest.cc +++ b/ash/display/mouse_cursor_event_filter_unittest.cc
@@ -172,13 +172,12 @@ aura::Window::Windows root_windows = Shell::GetAllRootWindows(); aura::Env::GetInstance()->set_last_mouse_location(gfx::Point(1, 1)); - event_filter()->set_mouse_warp_mode(MouseCursorEventFilter::WARP_NONE); + event_filter()->set_mouse_warp_enabled(false); EXPECT_FALSE(WarpMouseCursorIfNecessary(root_windows[0], gfx::Point(499, 11))); EXPECT_EQ("1,1", aura::Env::GetInstance()->last_mouse_location().ToString()); - - event_filter()->set_mouse_warp_mode(MouseCursorEventFilter::WARP_ALWAYS); + event_filter()->set_mouse_warp_enabled(true); EXPECT_TRUE(WarpMouseCursorIfNecessary(root_windows[0], gfx::Point(499, 11))); EXPECT_EQ("501,11", aura::Env::GetInstance()->last_mouse_location().ToString());
diff --git a/ash/display/resolution_notification_controller.cc b/ash/display/resolution_notification_controller.cc index bea5554..a0e98ea 100644 --- a/ash/display/resolution_notification_controller.cc +++ b/ash/display/resolution_notification_controller.cc
@@ -139,7 +139,7 @@ accept_callback(accept_callback), timeout_count(0) { DisplayManager* display_manager = Shell::GetInstance()->display_manager(); - if (!display_manager->HasInternalDisplay() && + if (!gfx::Display::HasInternalDisplay() && display_manager->num_connected_displays() == 1u) { timeout_count = kTimeoutInSec; }
diff --git a/ash/display/screen_ash.cc b/ash/display/screen_ash.cc index 271b8e62..700abe6 100644 --- a/ash/display/screen_ash.cc +++ b/ash/display/screen_ash.cc
@@ -146,7 +146,7 @@ } std::vector<gfx::Display> ScreenAsh::GetAllDisplays() const { - return GetDisplayManager()->displays(); + return GetDisplayManager()->active_display_list(); } gfx::Display ScreenAsh::GetDisplayNearestWindow(gfx::NativeView window) const { @@ -165,8 +165,8 @@ DisplayManager* display_manager = GetDisplayManager(); // RootWindow needs Display to determine its device scale factor // for non desktop display. - if (display_manager->mirroring_display().id() == id) - return display_manager->mirroring_display(); + if (display_manager->software_mirroring_display().id() == id) + return display_manager->software_mirroring_display(); return display_manager->GetDisplayForId(id); } @@ -178,14 +178,15 @@ // Fallback to the display that has the shortest Manhattan distance from // the |point|. This is correct in the only areas that matter, namely in the // corners between the physical screens. - return FindDisplayNearestPoint(GetDisplayManager()->displays(), point); + return FindDisplayNearestPoint(GetDisplayManager()->active_display_list(), + point); } gfx::Display ScreenAsh::GetDisplayMatching(const gfx::Rect& match_rect) const { if (match_rect.IsEmpty()) return GetDisplayNearestPoint(match_rect.origin()); - const gfx::Display* matching = - FindDisplayMatching(GetDisplayManager()->displays(), match_rect); + const gfx::Display* matching = FindDisplayMatching( + GetDisplayManager()->active_display_list(), match_rect); // Fallback to the primary display if there is no matching display. return matching ? *matching : GetPrimaryDisplay(); }
diff --git a/ash/host/ash_remote_window_tree_host_win.h b/ash/host/ash_remote_window_tree_host_win.h index 8c97db92..143a0ce 100644 --- a/ash/host/ash_remote_window_tree_host_win.h +++ b/ash/host/ash_remote_window_tree_host_win.h
@@ -21,22 +21,22 @@ explicit AshRemoteWindowTreeHostWin(HWND remote_hwnd); private: - virtual ~AshRemoteWindowTreeHostWin(); + ~AshRemoteWindowTreeHostWin() override; // AshWindowTreeHost: - virtual void ToggleFullScreen() override; - virtual bool ConfineCursorToRootWindow() override; - virtual void UnConfineCursor() override; - virtual void SetRootWindowTransformer( + void ToggleFullScreen() override; + bool ConfineCursorToRootWindow() override; + void UnConfineCursor() override; + void SetRootWindowTransformer( scoped_ptr<RootWindowTransformer> transformer) override; - virtual gfx::Insets GetHostInsets() const override; - virtual aura::WindowTreeHost* AsWindowTreeHost() override; + gfx::Insets GetHostInsets() const override; + aura::WindowTreeHost* AsWindowTreeHost() override; // WindowTreeHostWin: - virtual gfx::Transform GetRootTransform() const override; - virtual void SetRootTransform(const gfx::Transform& transform) override; - virtual gfx::Transform GetInverseRootTransform() const override; - virtual void UpdateRootWindowSize(const gfx::Size& host_size) override; + gfx::Transform GetRootTransform() const override; + void SetRootTransform(const gfx::Transform& transform) override; + gfx::Transform GetInverseRootTransform() const override; + void UpdateRootWindowSize(const gfx::Size& host_size) override; TransformerHelper transformer_helper_;
diff --git a/ash/host/ash_window_tree_host_win.cc b/ash/host/ash_window_tree_host_win.cc index d8ab45b..9d0988de 100644 --- a/ash/host/ash_window_tree_host_win.cc +++ b/ash/host/ash_window_tree_host_win.cc
@@ -28,11 +28,11 @@ saved_window_style_(0), saved_window_ex_style_(0), transformer_helper_(this) {} - virtual ~AshWindowTreeHostWin() {} + ~AshWindowTreeHostWin() override {} private: // AshWindowTreeHost: - virtual void ToggleFullScreen() override { + void ToggleFullScreen() override { gfx::Rect target_rect; if (!fullscreen_) { fullscreen_ = true; @@ -66,19 +66,18 @@ target_rect.height(), SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED); } - virtual bool ConfineCursorToRootWindow() override { return false; } - virtual void UnConfineCursor() override { NOTIMPLEMENTED(); } - virtual void SetRootWindowTransformer( - scoped_ptr<RootWindowTransformer> transformer) { + bool ConfineCursorToRootWindow() override { return false; } + void UnConfineCursor() override { NOTIMPLEMENTED(); } + void SetRootWindowTransformer(scoped_ptr<RootWindowTransformer> transformer) { transformer_helper_.SetRootWindowTransformer(transformer.Pass()); } - virtual gfx::Insets GetHostInsets() const override { + gfx::Insets GetHostInsets() const override { return transformer_helper_.GetHostInsets(); } - virtual aura::WindowTreeHost* AsWindowTreeHost() override { return this; } + aura::WindowTreeHost* AsWindowTreeHost() override { return this; } // WindowTreeHostWin: - virtual void SetBounds(const gfx::Rect& bounds) override { + void SetBounds(const gfx::Rect& bounds) override { if (fullscreen_) { saved_window_rect_.right = saved_window_rect_.left + bounds.width(); saved_window_rect_.bottom = saved_window_rect_.top + bounds.height(); @@ -86,16 +85,16 @@ } WindowTreeHostWin::SetBounds(bounds); } - virtual void SetRootTransform(const gfx::Transform& transform) override { + void SetRootTransform(const gfx::Transform& transform) override { transformer_helper_.SetTransform(transform); } gfx::Transform GetRootTransform() const { return transformer_helper_.GetTransform(); } - virtual gfx::Transform GetInverseRootTransform() const override { + gfx::Transform GetInverseRootTransform() const override { return transformer_helper_.GetInverseTransform(); } - virtual void UpdateRootWindowSize(const gfx::Size& host_size) override { + void UpdateRootWindowSize(const gfx::Size& host_size) override { transformer_helper_.UpdateWindowSize(host_size); }
diff --git a/ash/metrics/user_metrics_recorder.cc b/ash/metrics/user_metrics_recorder.cc index c1c9e63..ccdcb8f4 100644 --- a/ash/metrics/user_metrics_recorder.cc +++ b/ash/metrics/user_metrics_recorder.cc
@@ -203,6 +203,9 @@ case ash::UMA_LAUNCHER_LAUNCH_TASK: base::RecordAction(base::UserMetricsAction("Launcher_LaunchTask")); break; + case ash::UMA_LAUNCHER_SWITCH_TASK: + base::RecordAction(base::UserMetricsAction("Launcher_SwitchTask")); + break; case UMA_MAXIMIZE_MODE_DISABLED: base::RecordAction(base::UserMetricsAction("Touchview_Disabled")); break;
diff --git a/ash/metrics/user_metrics_recorder.h b/ash/metrics/user_metrics_recorder.h index da39a106..7c27326 100644 --- a/ash/metrics/user_metrics_recorder.h +++ b/ash/metrics/user_metrics_recorder.h
@@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef ASH_USER_METRICS_RECORDER_H_ -#define ASH_USER_METRICS_RECORDER_H_ +#ifndef ASH_METRICS_USER_METRICS_RECORDER_H_ +#define ASH_METRICS_USER_METRICS_RECORDER_H_ #include "ash/ash_export.h" #include "base/timer/timer.h" @@ -30,6 +30,7 @@ UMA_LAUNCHER_CLICK_ON_APP, UMA_LAUNCHER_CLICK_ON_APPLIST_BUTTON, UMA_LAUNCHER_LAUNCH_TASK, + UMA_LAUNCHER_SWITCH_TASK, UMA_MAXIMIZE_MODE_DISABLED, UMA_MAXIMIZE_MODE_ENABLED, UMA_MAXIMIZE_MODE_INITIALLY_DISABLED, @@ -139,4 +140,4 @@ } // namespace ash -#endif // ASH_USER_METRICS_RECORDER_H_ +#endif // ASH_METRICS_USER_METRICS_RECORDER_H_
diff --git a/ash/resources/ash_resources.grd b/ash/resources/ash_resources.grd index 62b9159..d2ebea9 100644 --- a/ash/resources/ash_resources.grd +++ b/ash/resources/ash_resources.grd
@@ -80,6 +80,11 @@ <structure type="chrome_scaled_image" name="IDR_AURA_UBER_TRAY_BLUETOOTH_DISABLED_HOVER" file="cros/status/status_bluetooth_disabled_hover.png" /> <structure type="chrome_scaled_image" name="IDR_AURA_UBER_TRAY_BLUETOOTH_ENABLED" file="cros/status/status_bluetooth_enabled.png" /> <structure type="chrome_scaled_image" name="IDR_AURA_UBER_TRAY_BLUETOOTH_ENABLED_HOVER" file="cros/status/status_bluetooth_enabled_hover.png" /> + <structure type="chrome_scaled_image" name="IDR_AURA_UBER_TRAY_CAST" file="cros/status/status_cast.png" /> + <structure type="chrome_scaled_image" name="IDR_AURA_UBER_TRAY_CAST_DISABLED" file="cros/status/status_cast_disabled.png" /> + <structure type="chrome_scaled_image" name="IDR_AURA_UBER_TRAY_CAST_DISABLED_HOVER" file="cros/status/status_cast_disabled_hover.png" /> + <structure type="chrome_scaled_image" name="IDR_AURA_UBER_TRAY_CAST_ENABLED" file="cros/status/status_cast_enabled.png" /> + <structure type="chrome_scaled_image" name="IDR_AURA_UBER_TRAY_CAST_ENABLED_HOVER" file="cros/status/status_cast_enabled_hover.png" /> <structure type="chrome_scaled_image" name="IDR_AURA_UBER_TRAY_DRIVE" file="cros/status/status_drive.png" /> <structure type="chrome_scaled_image" name="IDR_AURA_UBER_TRAY_DRIVE_CANCEL" file="cros/status/status_drive_item_cancel.png" /> <structure type="chrome_scaled_image" name="IDR_AURA_UBER_TRAY_DRIVE_CANCEL_HOVER" file="cros/status/status_drive_item_cancel_hover.png" />
diff --git a/ash/resources/default_100_percent/cros/status/status_cast.png b/ash/resources/default_100_percent/cros/status/status_cast.png new file mode 100644 index 0000000..d5694f6 --- /dev/null +++ b/ash/resources/default_100_percent/cros/status/status_cast.png Binary files differ
diff --git a/ash/resources/default_100_percent/cros/status/status_cast_disabled.png b/ash/resources/default_100_percent/cros/status/status_cast_disabled.png new file mode 100644 index 0000000..7f91cc3 --- /dev/null +++ b/ash/resources/default_100_percent/cros/status/status_cast_disabled.png Binary files differ
diff --git a/ash/resources/default_100_percent/cros/status/status_cast_disabled_hover.png b/ash/resources/default_100_percent/cros/status/status_cast_disabled_hover.png new file mode 100644 index 0000000..7f91cc3 --- /dev/null +++ b/ash/resources/default_100_percent/cros/status/status_cast_disabled_hover.png Binary files differ
diff --git a/ash/resources/default_100_percent/cros/status/status_cast_enabled.png b/ash/resources/default_100_percent/cros/status/status_cast_enabled.png new file mode 100644 index 0000000..cbfdc15 --- /dev/null +++ b/ash/resources/default_100_percent/cros/status/status_cast_enabled.png Binary files differ
diff --git a/ash/resources/default_100_percent/cros/status/status_cast_enabled_hover.png b/ash/resources/default_100_percent/cros/status/status_cast_enabled_hover.png new file mode 100644 index 0000000..cbfdc15 --- /dev/null +++ b/ash/resources/default_100_percent/cros/status/status_cast_enabled_hover.png Binary files differ
diff --git a/ash/resources/default_200_percent/cros/status/status_cast.png b/ash/resources/default_200_percent/cros/status/status_cast.png new file mode 100644 index 0000000..7420999 --- /dev/null +++ b/ash/resources/default_200_percent/cros/status/status_cast.png Binary files differ
diff --git a/ash/resources/default_200_percent/cros/status/status_cast_disabled.png b/ash/resources/default_200_percent/cros/status/status_cast_disabled.png new file mode 100644 index 0000000..f52ea74 --- /dev/null +++ b/ash/resources/default_200_percent/cros/status/status_cast_disabled.png Binary files differ
diff --git a/ash/resources/default_200_percent/cros/status/status_cast_disabled_hover.png b/ash/resources/default_200_percent/cros/status/status_cast_disabled_hover.png new file mode 100644 index 0000000..f52ea74 --- /dev/null +++ b/ash/resources/default_200_percent/cros/status/status_cast_disabled_hover.png Binary files differ
diff --git a/ash/resources/default_200_percent/cros/status/status_cast_enabled.png b/ash/resources/default_200_percent/cros/status/status_cast_enabled.png new file mode 100644 index 0000000..be30650 --- /dev/null +++ b/ash/resources/default_200_percent/cros/status/status_cast_enabled.png Binary files differ
diff --git a/ash/resources/default_200_percent/cros/status/status_cast_enabled_hover.png b/ash/resources/default_200_percent/cros/status/status_cast_enabled_hover.png new file mode 100644 index 0000000..be30650 --- /dev/null +++ b/ash/resources/default_200_percent/cros/status/status_cast_enabled_hover.png Binary files differ
diff --git a/ash/shelf/shelf_view.cc b/ash/shelf/shelf_view.cc index 21651b4..06d6dd4 100644 --- a/ash/shelf/shelf_view.cc +++ b/ash/shelf/shelf_view.cc
@@ -20,7 +20,6 @@ #include "ash/shelf/shelf_constants.h" #include "ash/shelf/shelf_delegate.h" #include "ash/shelf/shelf_icon_observer.h" -#include "ash/shelf/shelf_item_delegate.h" #include "ash/shelf/shelf_item_delegate_manager.h" #include "ash/shelf/shelf_layout_manager.h" #include "ash/shelf/shelf_menu_model.h" @@ -309,16 +308,6 @@ } } -void RecordIconActivatedAction(const ui::Event& event) { - if (event.IsMouseEvent()) { - Shell::GetInstance()->metrics()->RecordUserMetricsAction( - UMA_LAUNCHER_BUTTON_PRESSED_WITH_MOUSE); - } else if (event.IsGestureEvent()) { - Shell::GetInstance()->metrics()->RecordUserMetricsAction( - UMA_LAUNCHER_BUTTON_PRESSED_WITH_TOUCH); - } -} - } // namespace // AnimationDelegate used when deleting an item. This steadily decreased the @@ -1693,7 +1682,7 @@ if (sender == overflow_button_) { ToggleOverflowBubble(); - RecordIconActivatedAction(event); + RecordIconActivatedSource(event); return; } @@ -1752,21 +1741,44 @@ break; } - RecordIconActivatedAction(event); + RecordIconActivatedSource(event); - switch (item_manager_->GetShelfItemDelegate(model_->items()[view_index].id) - ->ItemSelected(event)) { - case ShelfItemDelegate::kNoAction: - case ShelfItemDelegate::kExistingWindowActivated: - case ShelfItemDelegate::kExistingWindowMinimized: - case ShelfItemDelegate::kAppListMenuShown: - ShowListMenuForView(model_->items()[view_index], sender, event); - break; - case ShelfItemDelegate::kNewWindowCreated: - Shell::GetInstance()->metrics()->RecordUserMetricsAction( - UMA_LAUNCHER_LAUNCH_TASK); - break; - } + ShelfItemDelegate::PerformedAction performed_action = + item_manager_->GetShelfItemDelegate(model_->items()[view_index].id) + ->ItemSelected(event); + + RecordIconActivatedAction(performed_action); + + if (performed_action != ShelfItemDelegate::kNewWindowCreated) + ShowListMenuForView(model_->items()[view_index], sender, event); + } +} + +void ShelfView::RecordIconActivatedSource(const ui::Event& event) { + if (event.IsMouseEvent()) { + Shell::GetInstance()->metrics()->RecordUserMetricsAction( + UMA_LAUNCHER_BUTTON_PRESSED_WITH_MOUSE); + } else if (event.IsGestureEvent()) { + Shell::GetInstance()->metrics()->RecordUserMetricsAction( + UMA_LAUNCHER_BUTTON_PRESSED_WITH_TOUCH); + } +} + +void ShelfView::RecordIconActivatedAction( + ShelfItemDelegate::PerformedAction performed_action) { + switch (performed_action) { + case ShelfItemDelegate::kNoAction: + case ShelfItemDelegate::kExistingWindowMinimized: + case ShelfItemDelegate::kAppListMenuShown: + break; + case ShelfItemDelegate::kNewWindowCreated: + Shell::GetInstance()->metrics()->RecordUserMetricsAction( + UMA_LAUNCHER_LAUNCH_TASK); + break; + case ShelfItemDelegate::kExistingWindowActivated: + Shell::GetInstance()->metrics()->RecordUserMetricsAction( + UMA_LAUNCHER_SWITCH_TASK); + break; } }
diff --git a/ash/shelf/shelf_view.h b/ash/shelf/shelf_view.h index 037922e..56dd95bc 100644 --- a/ash/shelf/shelf_view.h +++ b/ash/shelf/shelf_view.h
@@ -5,10 +5,12 @@ #ifndef ASH_SHELF_SHELF_VIEW_H_ #define ASH_SHELF_SHELF_VIEW_H_ +#include <string> #include <utility> #include <vector> #include "ash/shelf/shelf_button_host.h" +#include "ash/shelf/shelf_item_delegate.h" #include "ash/shelf/shelf_model_observer.h" #include "ash/wm/gestures/shelf_gesture_handler.h" #include "base/observer_list.h" @@ -276,6 +278,13 @@ // Overridden from views::ButtonListener: void ButtonPressed(views::Button* sender, const ui::Event& event) override; + // Records UMA statistics for the input source when an icon was activated. + void RecordIconActivatedSource(const ui::Event& event); + + // Records UMA statistics for the action performed by activating an icon. + void RecordIconActivatedAction( + ShelfItemDelegate::PerformedAction performed_action); + // Show the list of all running items for this |item|. It will return true // when the menu was shown and false if there were no possible items to // choose from. |source| specifies the view which is responsible for showing
diff --git a/ash/shelf/shelf_view_unittest.cc b/ash/shelf/shelf_view_unittest.cc index 39afb9a..beb6620 100644 --- a/ash/shelf/shelf_view_unittest.cc +++ b/ash/shelf/shelf_view_unittest.cc
@@ -34,6 +34,8 @@ #include "base/compiler_specific.h" #include "base/memory/scoped_ptr.h" #include "base/strings/string_number_conversions.h" +#include "base/test/user_action_tester.h" +#include "base/time/time.h" #include "ui/aura/test/aura_test_base.h" #include "ui/aura/window.h" #include "ui/aura/window_event_dispatcher.h" @@ -43,6 +45,7 @@ #include "ui/events/event_constants.h" #include "ui/events/event_utils.h" #include "ui/events/test/event_generator.h" +#include "ui/gfx/geometry/point.h" #include "ui/views/view_model.h" #include "ui/views/widget/widget.h" #include "ui/views/widget/widget_delegate.h" @@ -121,14 +124,21 @@ // TestShelfItemDelegate which tracks whether it gets selected. class ShelfItemSelectionTracker : public TestShelfItemDelegate { public: - ShelfItemSelectionTracker() : TestShelfItemDelegate(NULL), selected_(false) { - } + ShelfItemSelectionTracker() + : TestShelfItemDelegate(NULL), + selected_(false), + item_selected_action_(kNoAction) {} ~ShelfItemSelectionTracker() override {} // Resets to the initial state. void Reset() { selected_ = false; } + void set_item_selected_action( + ShelfItemDelegate::PerformedAction item_selected_action) { + item_selected_action_ = item_selected_action; + } + // Returns true if the delegate was selected. bool WasSelected() { return selected_; @@ -138,12 +148,15 @@ ShelfItemDelegate::PerformedAction ItemSelected( const ui::Event& event) override { selected_ = true; - return kNoAction; + return item_selected_action_; } private: bool selected_; + // The action returned from ItemSelected(const ui::Event&). + ShelfItemDelegate::PerformedAction item_selected_action_; + DISALLOW_COPY_AND_ASSIGN(ShelfItemSelectionTracker); }; @@ -1762,6 +1775,82 @@ EXPECT_FALSE(Shell::GetInstance()->GetAppListTargetVisibility()); } +// Verifies that Launcher_ButtonPressed_* UMA user actions are recorded when an +// item is selected. +TEST_F(ShelfViewTest, + Launcher_ButtonPressedUserActionsRecordedWhenItemSelected) { + base::UserActionTester user_action_tester; + + ShelfID browser_shelf_id = model_->items()[browser_index_].id; + ShelfItemSelectionTracker* selection_tracker = new ShelfItemSelectionTracker; + item_manager_->SetShelfItemDelegate( + browser_shelf_id, + scoped_ptr<ShelfItemDelegate>(selection_tracker).Pass()); + + SimulateClick(browser_index_); + EXPECT_EQ(1, + user_action_tester.GetActionCount("Launcher_ButtonPressed_Mouse")); +} + +// Verifies that Launcher_*Task UMA user actions are recorded when an item is +// selected. +TEST_F(ShelfViewTest, Launcher_TaskUserActionsRecordedWhenItemSelected) { + base::UserActionTester user_action_tester; + + ShelfID browser_shelf_id = model_->items()[browser_index_].id; + ShelfItemSelectionTracker* selection_tracker = new ShelfItemSelectionTracker; + selection_tracker->set_item_selected_action( + ShelfItemDelegate::kNewWindowCreated); + item_manager_->SetShelfItemDelegate( + browser_shelf_id, + scoped_ptr<ShelfItemDelegate>(selection_tracker).Pass()); + + SimulateClick(browser_index_); + EXPECT_EQ(1, user_action_tester.GetActionCount("Launcher_LaunchTask")); +} + +// Verifies that a Launcher_ButtonPressed_Mouse UMA user action is recorded when +// an icon is activated by a mouse event. +TEST_F(ShelfViewTest, + Launcher_ButtonPressed_MouseIsRecordedWhenIconActivatedByMouse) { + base::UserActionTester user_action_tester; + ui::MouseEvent mouse_event(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(), + base::TimeDelta(), 0, 0); + test_api_->RecordIconActivatedSource(mouse_event); + EXPECT_EQ(1, + user_action_tester.GetActionCount("Launcher_ButtonPressed_Mouse")); +} + +// Verifies that a Launcher_ButtonPressed_Touch UMA user action is recorded when +// an icon is activated by a touch event. +TEST_F(ShelfViewTest, + Launcher_ButtonPressed_MouseIsRecordedWhenIconActivatedByTouch) { + base::UserActionTester user_action_tester; + ui::TouchEvent touch_event(ui::ET_GESTURE_TAP, gfx::Point(), 0, + base::TimeDelta()); + test_api_->RecordIconActivatedSource(touch_event); + EXPECT_EQ(1, + user_action_tester.GetActionCount("Launcher_ButtonPressed_Touch")); +} + +// Verifies that a Launcher_LaunchTask UMA user action is recorded when +// selecting an icon causes a new window to be created. +TEST_F(ShelfViewTest, Launcher_LaunchTaskIsRecordedWhenNewWindowIsCreated) { + base::UserActionTester user_action_tester; + test_api_->RecordIconActivatedAction(ShelfItemDelegate::kNewWindowCreated); + EXPECT_EQ(1, user_action_tester.GetActionCount("Launcher_LaunchTask")); +} + +// Verifies that a Launcher_SwitchTask UMA user action is recorded when +// selecting an icon causes an existing window to be activated. +TEST_F(ShelfViewTest, + Launcher_SwitchTaskIsRecordedWhenExistingWindowIsActivated) { + base::UserActionTester user_action_tester; + test_api_->RecordIconActivatedAction( + ShelfItemDelegate::kExistingWindowActivated); + EXPECT_EQ(1, user_action_tester.GetActionCount("Launcher_SwitchTask")); +} + class ShelfViewVisibleBoundsTest : public ShelfViewTest, public testing::WithParamInterface<bool> { public:
diff --git a/ash/system/chromeos/brightness/tray_brightness.cc b/ash/system/chromeos/brightness/tray_brightness.cc index e874ccf..3e8b330 100644 --- a/ash/system/chromeos/brightness/tray_brightness.cc +++ b/ash/system/chromeos/brightness/tray_brightness.cc
@@ -8,7 +8,6 @@ #include "ash/accelerators/accelerator_controller.h" #include "ash/ash_constants.h" -#include "ash/display/display_manager.h" #include "ash/metrics/user_metrics_recorder.h" #include "ash/shell.h" #include "ash/shell_observer.h" @@ -26,6 +25,7 @@ #include "grit/ash_resources.h" #include "grit/ash_strings.h" #include "ui/base/resource/resource_bundle.h" +#include "ui/gfx/display.h" #include "ui/gfx/image/image.h" #include "ui/views/controls/button/image_button.h" #include "ui/views/controls/image_view.h" @@ -273,7 +273,7 @@ // Never show the bubble on systems that lack internal displays: if an // external display's brightness is changed, it may already display the new // level via an on-screen display. - if (!Shell::GetInstance()->display_manager()->HasInternalDisplay()) + if (!gfx::Display::HasInternalDisplay()) return; if (brightness_view_)
diff --git a/ash/system/chromeos/tray_display.cc b/ash/system/chromeos/tray_display.cc index 10f9ede..aaeead2 100644 --- a/ash/system/chromeos/tray_display.cc +++ b/ash/system/chromeos/tray_display.cc
@@ -52,9 +52,9 @@ // We don't show display size for mirrored display. Fallback // to empty string if this happens on release build. - bool mirrored_display = display_manager->mirrored_display_id() == display_id; - DCHECK(!mirrored_display); - if (mirrored_display) + bool mirroring = display_manager->mirroring_display_id() == display_id; + DCHECK(!mirroring); + if (mirroring) return base::string16(); DCHECK(display->is_valid()); @@ -66,7 +66,7 @@ base::string16 GetDisplayInfoLine(int64 display_id) { const DisplayInfo& display_info = GetDisplayManager()->GetDisplayInfo(display_id); - if (GetDisplayManager()->mirrored_display_id() == display_id) + if (GetDisplayManager()->mirroring_display_id() == display_id) return GetDisplayName(display_id); base::string16 size_text = GetDisplaySize(display_id); @@ -92,9 +92,9 @@ std::vector<base::string16> lines; int64 internal_id = gfx::Display::kInvalidDisplayID; // Make sure to show the internal display first. - if (display_manager->HasInternalDisplay() && - display_manager->IsInternalDisplayId( - display_manager->first_display_id())) { + if (gfx::Display::HasInternalDisplay() && + gfx::Display::InternalDisplayId() == + display_manager->first_display_id()) { internal_id = display_manager->first_display_id(); lines.push_back(GetDisplayInfoLine(internal_id)); } @@ -186,7 +186,7 @@ // mirroring. static base::string16 GetExternalDisplayName() { DisplayManager* display_manager = GetDisplayManager(); - DCHECK(!display_manager->IsMirrored()); + DCHECK(!display_manager->IsInMirrorMode()); int64 external_id = gfx::Display::kInvalidDisplayID; for (size_t i = 0; i < display_manager->GetNumDisplays(); ++i) { @@ -228,7 +228,7 @@ base::string16* additional_message_out) { DisplayManager* display_manager = GetDisplayManager(); if (display_manager->GetNumDisplays() > 1) { - if (GetDisplayManager()->HasInternalDisplay()) { + if (gfx::Display::HasInternalDisplay()) { return l10n_util::GetStringFUTF16( IDS_ASH_STATUS_TRAY_DISPLAY_EXTENDED, GetExternalDisplayName()); } @@ -236,19 +236,19 @@ IDS_ASH_STATUS_TRAY_DISPLAY_EXTENDED_NO_INTERNAL); } - if (display_manager->IsMirrored()) { - if (GetDisplayManager()->HasInternalDisplay()) { + if (display_manager->IsInMirrorMode()) { + if (gfx::Display::HasInternalDisplay()) { return l10n_util::GetStringFUTF16( IDS_ASH_STATUS_TRAY_DISPLAY_MIRRORING, - GetDisplayName(display_manager->mirrored_display_id())); + GetDisplayName(display_manager->mirroring_display_id())); } return l10n_util::GetStringUTF16( IDS_ASH_STATUS_TRAY_DISPLAY_MIRRORING_NO_INTERNAL); } int64 primary_id = Shell::GetScreen()->GetPrimaryDisplay().id(); - if (display_manager->HasInternalDisplay() && - !display_manager->IsInternalDisplayId(primary_id)) { + if (gfx::Display::HasInternalDisplay() && + !(gfx::Display::InternalDisplayId() == primary_id)) { if (additional_message_out) { *additional_message_out = l10n_util::GetStringUTF16( IDS_ASH_STATUS_TRAY_DISPLAY_DOCKED_DESCRIPTION);
diff --git a/ash/system/chromeos/tray_display_unittest.cc b/ash/system/chromeos/tray_display_unittest.cc index d2a1641..e289fa24 100644 --- a/ash/system/chromeos/tray_display_unittest.cc +++ b/ash/system/chromeos/tray_display_unittest.cc
@@ -65,10 +65,10 @@ ScreenUtil::GetSecondaryDisplay().id())); } -base::string16 GetMirroredDisplayName() { +base::string16 GetMirroringDisplayName() { DisplayManager* display_manager = Shell::GetInstance()->display_manager(); return base::UTF8ToUTF16(display_manager->GetDisplayNameForId( - display_manager->mirrored_display_id())); + display_manager->mirroring_display_id())); } class TrayDisplayTest : public ash::test::AshTestBase { @@ -228,8 +228,8 @@ tray()->ShowDefaultView(BUBBLE_USE_EXISTING); EXPECT_TRUE(IsDisplayVisibleInTray()); - expected = l10n_util::GetStringFUTF16( - IDS_ASH_STATUS_TRAY_DISPLAY_MIRRORING, GetMirroredDisplayName()); + expected = l10n_util::GetStringFUTF16(IDS_ASH_STATUS_TRAY_DISPLAY_MIRRORING, + GetMirroringDisplayName()); EXPECT_EQ(expected, GetTrayDisplayText()); EXPECT_EQ(GetMirroredTooltipText(expected, GetFirstDisplayName(), "400x400"), GetTrayDisplayTooltipText()); @@ -270,8 +270,8 @@ UpdateDisplay("400x400@1.5,200x200"); tray()->ShowDefaultView(BUBBLE_USE_EXISTING); EXPECT_TRUE(IsDisplayVisibleInTray()); - expected = l10n_util::GetStringFUTF16( - IDS_ASH_STATUS_TRAY_DISPLAY_MIRRORING, GetMirroredDisplayName()); + expected = l10n_util::GetStringFUTF16(IDS_ASH_STATUS_TRAY_DISPLAY_MIRRORING, + GetMirroringDisplayName()); EXPECT_EQ(expected, GetTrayDisplayText()); EXPECT_EQ(GetMirroredTooltipText(expected, GetFirstDisplayName(), "600x600"), GetTrayDisplayTooltipText()); @@ -325,8 +325,8 @@ UpdateDisplay("400x400,200x200@1.5"); tray()->ShowDefaultView(BUBBLE_USE_EXISTING); EXPECT_TRUE(IsDisplayVisibleInTray()); - expected = l10n_util::GetStringFUTF16( - IDS_ASH_STATUS_TRAY_DISPLAY_MIRRORING, GetMirroredDisplayName()); + expected = l10n_util::GetStringFUTF16(IDS_ASH_STATUS_TRAY_DISPLAY_MIRRORING, + GetMirroringDisplayName()); EXPECT_EQ(expected, GetTrayDisplayText()); EXPECT_EQ(GetMirroredTooltipText(expected, GetFirstDisplayName(), "400x400"), GetTrayDisplayTooltipText()); @@ -480,10 +480,9 @@ CloseNotification(); display_manager->SetSoftwareMirroring(true); UpdateDisplay("400x400,200x200"); - EXPECT_EQ( - l10n_util::GetStringFUTF16( - IDS_ASH_STATUS_TRAY_DISPLAY_MIRRORING, GetMirroredDisplayName()), - GetDisplayNotificationText()); + EXPECT_EQ(l10n_util::GetStringFUTF16(IDS_ASH_STATUS_TRAY_DISPLAY_MIRRORING, + GetMirroringDisplayName()), + GetDisplayNotificationText()); EXPECT_TRUE(GetDisplayNotificationAdditionalText().empty()); // Back to extended.
diff --git a/ash/system/overview/overview_button_tray_unittest.cc b/ash/system/overview/overview_button_tray_unittest.cc index 8a6fc5e..516a5ba 100644 --- a/ash/system/overview/overview_button_tray_unittest.cc +++ b/ash/system/overview/overview_button_tray_unittest.cc
@@ -4,8 +4,10 @@ #include "ash/system/overview/overview_button_tray.h" +#include "ash/ash_switches.h" #include "ash/display/display_manager.h" #include "ash/root_window_controller.h" +#include "ash/rotator/screen_rotation_animator.h" #include "ash/shelf/shelf_types.h" #include "ash/shelf/shelf_widget.h" #include "ash/shell.h" @@ -15,7 +17,9 @@ #include "ash/test/status_area_widget_test_helper.h" #include "ash/wm/maximize_mode/maximize_mode_controller.h" #include "ash/wm/overview/window_selector_controller.h" +#include "base/command_line.h" #include "base/time/time.h" +#include "ui/compositor/scoped_animation_duration_scale_mode.h" #include "ui/events/event.h" #include "ui/events/event_constants.h" #include "ui/events/gestures/gesture_types.h" @@ -42,6 +46,8 @@ OverviewButtonTrayTest() {} ~OverviewButtonTrayTest() override {} + void SetUp() override; + protected: views::ImageView* GetImageView(OverviewButtonTray* tray) { return tray->icon_; @@ -51,6 +57,14 @@ DISALLOW_COPY_AND_ASSIGN(OverviewButtonTrayTest); }; +void OverviewButtonTrayTest::SetUp() { + base::CommandLine::ForCurrentProcess()->AppendSwitch( + switches::kAshUseFirstDisplayAsInternal); + base::CommandLine::ForCurrentProcess()->AppendSwitch( + switches::kAshEnableScreenRotationAnimation); + AshTestBase::SetUp(); +} + // Ensures that creation doesn't cause any crashes and adds the image icon. TEST_F(OverviewButtonTrayTest, BasicConstruction) { EXPECT_TRUE(GetImageView(GetTray()) != NULL); @@ -156,4 +170,29 @@ EXPECT_FALSE(GetTray()->draw_background_as_active()); } +// Test that when a hide animation is aborted via deletion, that the +// OverviewButton is still hidden. +TEST_F(OverviewButtonTrayTest, HideAnimationAlwaysCompletes) { + Shell::GetInstance() + ->maximize_mode_controller() + ->EnableMaximizeModeWindowManager(true); + + // Long duration for hide animation, to allow it to be interrupted. + scoped_ptr<ui::ScopedAnimationDurationScaleMode> hide_duration( + new ui::ScopedAnimationDurationScaleMode( + ui::ScopedAnimationDurationScaleMode::SLOW_DURATION)); + GetTray()->SetVisible(false); + + // ScreenRotationAnimator copies the current layers, and deletes them upon + // completion. Allow its animation to complete first. + scoped_ptr<ui::ScopedAnimationDurationScaleMode> rotate_duration( + new ui::ScopedAnimationDurationScaleMode( + ui::ScopedAnimationDurationScaleMode::ZERO_DURATION)); + ash::ScreenRotationAnimator(gfx::Display::InternalDisplayId()) + .Rotate(gfx::Display::ROTATE_270); + + RunAllPendingInMessageLoop(); + EXPECT_FALSE(GetTray()->visible()); +} + } // namespace ash
diff --git a/ash/system/system_notifier.cc b/ash/system/system_notifier.cc index 6ff6dcb..c762930 100644 --- a/ash/system/system_notifier.cc +++ b/ash/system/system_notifier.cc
@@ -27,6 +27,7 @@ kNotifierDisplayError, #if defined(OS_CHROMEOS) ui::NetworkStateNotifier::kNotifierNetworkError, + kNotifierOobeScreen, #endif kNotifierPower, // Note: Order doesn't matter here, so keep this in alphabetic order, don't @@ -76,6 +77,7 @@ const char kNotifierLocale[] = "ash.locale"; const char kNotifierMultiProfileFirstRun[] = "ash.multi-profile.first-run"; const char kNotifierNetworkPortalDetector[] = "ash.network.portal-detector"; +const char kNotifierOobeScreen[] = "ash.oobe-screen"; const char kNotifierPower[] = "ash.power"; const char kNotifierScreenshot[] = "ash.screenshot"; const char kNotifierScreenCapture[] = "ash.screen-capture";
diff --git a/ash/system/system_notifier.h b/ash/system/system_notifier.h index 9461389..540f328 100644 --- a/ash/system/system_notifier.h +++ b/ash/system/system_notifier.h
@@ -24,6 +24,7 @@ ASH_EXPORT extern const char kNotifierNetwork[]; ASH_EXPORT extern const char kNotifierNetworkError[]; ASH_EXPORT extern const char kNotifierNetworkPortalDetector[]; +ASH_EXPORT extern const char kNotifierOobeScreen[]; ASH_EXPORT extern const char kNotifierPower[]; ASH_EXPORT extern const char kNotifierScreenshot[]; ASH_EXPORT extern const char kNotifierScreenCapture[];
diff --git a/ash/system/tray/tray_background_view.cc b/ash/system/tray/tray_background_view.cc index 9b4afd3..53e116f0 100644 --- a/ash/system/tray/tray_background_view.cc +++ b/ash/system/tray/tray_background_view.cc
@@ -256,6 +256,7 @@ TrayBackgroundView::~TrayBackgroundView() { if (GetWidget()) GetWidget()->RemoveObserver(widget_observer_.get()); + StopObservingImplicitAnimations(); } void TrayBackgroundView::Initialize() { @@ -439,6 +440,15 @@ views::View::SetVisible(false); } +bool TrayBackgroundView::RequiresNotificationWhenAnimatorDestroyed() const { + // This is needed so that OnImplicitAnimationsCompleted() is called even upon + // destruction of the animator. This can occure when parallel animations + // caused by ScreenRotationAnimator end before the animations of + // TrayBackgroundView. This allows for a proper update to the visual state of + // the view. (crbug.com/476667) + return true; +} + void TrayBackgroundView::HideTransformation() { gfx::Transform transform; if (shelf_alignment_ == SHELF_ALIGNMENT_BOTTOM ||
diff --git a/ash/system/tray/tray_background_view.h b/ash/system/tray/tray_background_view.h index b424cfc..f1562b7 100644 --- a/ash/system/tray/tray_background_view.h +++ b/ash/system/tray/tray_background_view.h
@@ -158,6 +158,7 @@ // ui::ImplicitAnimationObserver: void OnImplicitAnimationsCompleted() override; + bool RequiresNotificationWhenAnimatorDestroyed() const override; // Applies transformations to the |layer()| to animate the view when // SetVisible(false) is called.
diff --git a/ash/system/win/audio/tray_audio_delegate_win.h b/ash/system/win/audio/tray_audio_delegate_win.h index a299b7d..9ab0208 100644 --- a/ash/system/win/audio/tray_audio_delegate_win.h +++ b/ash/system/win/audio/tray_audio_delegate_win.h
@@ -18,18 +18,18 @@ class ASH_EXPORT TrayAudioDelegateWin : public TrayAudioDelegate { public: - virtual ~TrayAudioDelegateWin() {} + ~TrayAudioDelegateWin() override {} // Overridden from TrayAudioDelegate. - virtual void AdjustOutputVolumeToAudibleLevel() override; - virtual int GetOutputDefaultVolumeMuteLevel() override; - virtual int GetOutputVolumeLevel() override; - virtual int GetActiveOutputDeviceIconId() override; - virtual bool HasAlternativeSources() override; - virtual bool IsOutputAudioMuted() override; - virtual void SetOutputAudioIsMuted(bool is_muted) override; - virtual void SetOutputVolumeLevel(int level) override; - virtual void SetInternalSpeakerChannelMode(AudioChannelMode mode) override; + void AdjustOutputVolumeToAudibleLevel() override; + int GetOutputDefaultVolumeMuteLevel() override; + int GetOutputVolumeLevel() override; + int GetActiveOutputDeviceIconId() override; + bool HasAlternativeSources() override; + bool IsOutputAudioMuted() override; + void SetOutputAudioIsMuted(bool is_muted) override; + void SetOutputVolumeLevel(int level) override; + void SetInternalSpeakerChannelMode(AudioChannelMode mode) override; private: base::win::ScopedComPtr<ISimpleAudioVolume> CreateDefaultVolumeControl();
diff --git a/ash/test/ash_test_base.cc b/ash/test/ash_test_base.cc index ef7fba3..6e6f78d 100644 --- a/ash/test/ash_test_base.cc +++ b/ash/test/ash_test_base.cc
@@ -214,8 +214,7 @@ DisplayManager* display_manager = Shell::GetInstance()->display_manager(); DisplayManagerTestApi display_manager_test_api(display_manager); display_manager_test_api.UpdateDisplay(display_specs); - if (display_manager->HasSoftwareMirroringDisplay()) - RunAllPendingInMessageLoop(); + display_manager->RunPendingTasksForTest(); } aura::Window* AshTestBase::CurrentContext() {
diff --git a/ash/test/display_manager_test_api.cc b/ash/test/display_manager_test_api.cc index 9bed696..87326ee 100644 --- a/ash/test/display_manager_test_api.cc +++ b/ash/test/display_manager_test_api.cc
@@ -82,7 +82,7 @@ } int64 DisplayManagerTestApi::SetFirstDisplayAsInternalDisplay() { - const gfx::Display& internal = display_manager_->displays_[0]; + const gfx::Display& internal = display_manager_->active_display_list_[0]; SetInternalDisplayId(internal.id()); return gfx::Display::InternalDisplayId(); }
diff --git a/ash/test/shelf_view_test_api.cc b/ash/test/shelf_view_test_api.cc index b829b87d5..715b986c 100644 --- a/ash/test/shelf_view_test_api.cc +++ b/ash/test/shelf_view_test_api.cc
@@ -118,6 +118,15 @@ return shelf_view_->ButtonPressed(sender, event); } +void ShelfViewTestAPI::RecordIconActivatedSource(const ui::Event& event) { + shelf_view_->RecordIconActivatedSource(event); +} + +void ShelfViewTestAPI::RecordIconActivatedAction( + ShelfItemDelegate::PerformedAction performed_action) { + shelf_view_->RecordIconActivatedAction(performed_action); +} + bool ShelfViewTestAPI::SameDragType(ShelfItemType typea, ShelfItemType typeb) const { return shelf_view_->SameDragType(typea, typeb);
diff --git a/ash/test/shelf_view_test_api.h b/ash/test/shelf_view_test_api.h index 7916ea7..04e5d40 100644 --- a/ash/test/shelf_view_test_api.h +++ b/ash/test/shelf_view_test_api.h
@@ -5,6 +5,7 @@ #ifndef ASH_TEST_SHELF_VIEW_TEST_API_H_ #define ASH_TEST_SHELF_VIEW_TEST_API_H_ +#include "ash/shelf/shelf_item_delegate.h" #include "ash/shelf/shelf_item_types.h" #include "base/basictypes.h" @@ -81,6 +82,14 @@ // Wrapper for ShelfView::ButtonPressed. void ButtonPressed(views::Button* sender, const ui::Event& event); + // Wrapper for ShelfView::RecordIconActivatedSource(const ui::Event&). + void RecordIconActivatedSource(const ui::Event& event); + + // Wrapper for ShelfView::RecordIconActivatedAction( + // ShelfItemDelegate::PerformedAction). + void RecordIconActivatedAction( + ShelfItemDelegate::PerformedAction performed_action); + // Wrapper for ShelfView::SameDragType. bool SameDragType(ShelfItemType typea, ShelfItemType typeb) const;
diff --git a/ash/test/test_metro_viewer_process_host.h b/ash/test/test_metro_viewer_process_host.h index bb393515..a25095ef 100644 --- a/ash/test/test_metro_viewer_process_host.h +++ b/ash/test/test_metro_viewer_process_host.h
@@ -17,7 +17,7 @@ public: TestMetroViewerProcessHost( const scoped_refptr<base::SingleThreadTaskRunner>& ipc_task_runner); - virtual ~TestMetroViewerProcessHost(); + ~TestMetroViewerProcessHost() override; bool closed_unexpectedly() { return closed_unexpectedly_; } @@ -27,13 +27,12 @@ private: // win8::MetroViewerProcessHost implementation - virtual void OnChannelError() override; - virtual void OnSetTargetSurface(gfx::NativeViewId target_surface, - float device_scale) override; - virtual void OnOpenURL(const base::string16& url) override; - virtual void OnHandleSearchRequest( - const base::string16& search_string) override; - virtual void OnWindowSizeChanged(uint32 width, uint32 height) override; + void OnChannelError() override; + void OnSetTargetSurface(gfx::NativeViewId target_surface, + float device_scale) override; + void OnOpenURL(const base::string16& url) override; + void OnHandleSearchRequest(const base::string16& search_string) override; + void OnWindowSizeChanged(uint32 width, uint32 height) override; bool closed_unexpectedly_;
diff --git a/ash/touch/touch_transformer_controller.cc b/ash/touch/touch_transformer_controller.cc index 8adb644..d2582c0 100644 --- a/ash/touch/touch_transformer_controller.cc +++ b/ash/touch/touch_transformer_controller.cc
@@ -174,7 +174,7 @@ gfx::Size fb_size = Shell::GetInstance()->display_configurator()->framebuffer_size(); - if (display_manager->IsMirrored()) { + if (display_manager->IsInMirrorMode()) { if (GetDisplayManager()->software_mirroring_enabled()) { // In extended but software mirroring mode, there is a WindowTreeHost for // each display, but all touches are forwarded to the primary root
diff --git a/ash/wm/drag_window_resizer.cc b/ash/wm/drag_window_resizer.cc index 1cb72e2..948137e 100644 --- a/ash/wm/drag_window_resizer.cc +++ b/ash/wm/drag_window_resizer.cc
@@ -57,8 +57,7 @@ if (window_state_) window_state_->DeleteDragDetails(); Shell* shell = Shell::GetInstance(); - shell->mouse_cursor_filter()->set_mouse_warp_mode( - MouseCursorEventFilter::WARP_ALWAYS); + shell->mouse_cursor_filter()->set_mouse_warp_enabled(true); shell->mouse_cursor_filter()->HideSharedEdgeIndicator(); if (instance_ == this) instance_ = NULL; @@ -156,9 +155,7 @@ // window/tab to another display. MouseCursorEventFilter* mouse_cursor_filter = Shell::GetInstance()->mouse_cursor_filter(); - mouse_cursor_filter->set_mouse_warp_mode( - ShouldAllowMouseWarp() ? - MouseCursorEventFilter::WARP_DRAG : MouseCursorEventFilter::WARP_NONE); + mouse_cursor_filter->set_mouse_warp_enabled(ShouldAllowMouseWarp()); if (ShouldAllowMouseWarp()) mouse_cursor_filter->ShowSharedEdgeIndicator(GetTarget()->GetRootWindow()); instance_ = this;
diff --git a/ash/wm/drag_window_resizer_unittest.cc b/ash/wm/drag_window_resizer_unittest.cc index 7429254..c5c5be5e 100644 --- a/ash/wm/drag_window_resizer_unittest.cc +++ b/ash/wm/drag_window_resizer_unittest.cc
@@ -427,49 +427,40 @@ ASSERT_TRUE(event_filter); window_->SetBounds(gfx::Rect(0, 0, 50, 60)); - EXPECT_EQ(MouseCursorEventFilter::WARP_ALWAYS, - event_filter->mouse_warp_mode_); + EXPECT_TRUE(event_filter->mouse_warp_enabled_); { scoped_ptr<WindowResizer> resizer(CreateDragWindowResizer( window_.get(), gfx::Point(), HTCAPTION)); // While dragging a window, warp should be allowed. - EXPECT_EQ(MouseCursorEventFilter::WARP_DRAG, - event_filter->mouse_warp_mode_); + EXPECT_TRUE(event_filter->mouse_warp_enabled_); resizer->CompleteDrag(); } - EXPECT_EQ(MouseCursorEventFilter::WARP_ALWAYS, - event_filter->mouse_warp_mode_); + EXPECT_TRUE(event_filter->mouse_warp_enabled_); { scoped_ptr<WindowResizer> resizer(CreateDragWindowResizer( window_.get(), gfx::Point(), HTCAPTION)); - EXPECT_EQ(MouseCursorEventFilter::WARP_DRAG, - event_filter->mouse_warp_mode_); + EXPECT_TRUE(event_filter->mouse_warp_enabled_); resizer->RevertDrag(); } - EXPECT_EQ(MouseCursorEventFilter::WARP_ALWAYS, - event_filter->mouse_warp_mode_); + EXPECT_TRUE(event_filter->mouse_warp_enabled_); { scoped_ptr<WindowResizer> resizer(CreateDragWindowResizer( window_.get(), gfx::Point(), HTRIGHT)); // While resizing a window, warp should NOT be allowed. - EXPECT_EQ(MouseCursorEventFilter::WARP_NONE, - event_filter->mouse_warp_mode_); + EXPECT_FALSE(event_filter->mouse_warp_enabled_); resizer->CompleteDrag(); } - EXPECT_EQ(MouseCursorEventFilter::WARP_ALWAYS, - event_filter->mouse_warp_mode_); + EXPECT_TRUE(event_filter->mouse_warp_enabled_); { scoped_ptr<WindowResizer> resizer(CreateDragWindowResizer( window_.get(), gfx::Point(), HTRIGHT)); - EXPECT_EQ(MouseCursorEventFilter::WARP_NONE, - event_filter->mouse_warp_mode_); + EXPECT_FALSE(event_filter->mouse_warp_enabled_); resizer->RevertDrag(); } - EXPECT_EQ(MouseCursorEventFilter::WARP_ALWAYS, - event_filter->mouse_warp_mode_); + EXPECT_TRUE(event_filter->mouse_warp_enabled_); } // Verifies cursor's device scale factor is updated whe a window is moved across
diff --git a/base/BUILD.gn b/base/BUILD.gn index 8a7acc4..7750eeec 100644 --- a/base/BUILD.gn +++ b/base/BUILD.gn
@@ -91,6 +91,8 @@ "android/important_file_writer_android.h", "android/java_handler_thread.cc", "android/java_handler_thread.h", + "android/java_runtime.cc", + "android/java_runtime.h", "android/jni_android.cc", "android/jni_android.h", "android/jni_array.cc", @@ -1410,9 +1412,20 @@ "android/java/src/org/chromium/base/metrics/RecordHistogram.java", "android/java/src/org/chromium/base/metrics/RecordUserAction.java", ] + + deps = [ + ":android_runtime_jni_headers", + ] + jni_package = "base" } + # GYP: //base.gyp:android_runtime_jni_headers + generate_jar_jni("android_runtime_jni_headers") { + jni_package = "base" + classes = [ "java/lang/Runtime.class" ] + } + # GYP: //base.gyp:base_java android_library("base_java") { srcjar_deps = [
diff --git a/base/android/base_jni_registrar.cc b/base/android/base_jni_registrar.cc index 46781e8..43ba635 100644 --- a/base/android/base_jni_registrar.cc +++ b/base/android/base_jni_registrar.cc
@@ -14,6 +14,7 @@ #include "base/android/field_trial_list.h" #include "base/android/important_file_writer_android.h" #include "base/android/java_handler_thread.h" +#include "base/android/java_runtime.h" #include "base/android/jni_android.h" #include "base/android/jni_registrar.h" #include "base/android/jni_utils.h" @@ -57,6 +58,7 @@ {"PowerMonitor", base::RegisterPowerMonitor}, {"RecordHistogram", base::android::RegisterRecordHistogram}, {"RecordUserAction", base::android::RegisterRecordUserAction}, + {"Runtime", base::android::JavaRuntime::Register}, {"SystemMessageHandler", base::MessagePumpForUI::RegisterBindings}, {"SysUtils", base::android::SysUtils::Register}, {"ThreadUtils", base::RegisterThreadUtils},
diff --git a/base/android/java/src/org/chromium/base/PathUtils.java b/base/android/java/src/org/chromium/base/PathUtils.java index bcd850c..5da080e 100644 --- a/base/android/java/src/org/chromium/base/PathUtils.java +++ b/base/android/java/src/org/chromium/base/PathUtils.java
@@ -16,8 +16,6 @@ */ public abstract class PathUtils { - private static String sDataDirectorySuffix; - private static final int DATA_DIRECTORY = 0; private static final int DATABASE_DIRECTORY = 1; private static final int CACHE_DIRECTORY = 2; @@ -28,30 +26,14 @@ private PathUtils() {} /** - * Sets the suffix that should be used for the directory where private data is to be stored - * by the application. - * - * TODO(wnwen): Remove this after all clients have migrated and add asserts for not null. - * - * @param suffix The private data directory suffix. - * @see Context#getDir(String, int) - * @deprecated - */ - @Deprecated - public static void setPrivateDataDirectorySuffix(String suffix) { - sDirPathFetchTask = null; - sDataDirectorySuffix = suffix; - } - - /** * Starts an asynchronous task to fetch the path of the directory where private data is to be * stored by the application. * * @param suffix The private data directory suffix. * @see Context#getDir(String, int) */ - public static void setPrivateDataDirectorySuffix(String suffix, final Context appContext) { - sDataDirectorySuffix = null; + public static void setPrivateDataDirectorySuffix(String suffix, Context context) { + final Context appContext = context.getApplicationContext(); sDirPathFetchTask = new AsyncTask<String, Void, String[]>() { @Override protected String[] doInBackground(String... dataDirectorySuffix) { @@ -83,15 +65,8 @@ */ @CalledByNative public static String getDataDirectory(Context appContext) { - if (sDataDirectorySuffix == null && sDirPathFetchTask == null) { - throw new IllegalStateException( - "setDataDirectorySuffix must be called before getDataDirectory"); - } else if (sDirPathFetchTask != null) { - return getDirectoryPath(DATA_DIRECTORY); - } else { - // Temporarily allow UI thread directory fetching until all callers have been migrated. - return appContext.getDir(sDataDirectorySuffix, Context.MODE_PRIVATE).getPath(); - } + assert sDirPathFetchTask != null : "setDataDirectorySuffix must be called first."; + return getDirectoryPath(DATA_DIRECTORY); } /** @@ -99,11 +74,8 @@ */ @CalledByNative public static String getDatabaseDirectory(Context appContext) { - if (sDirPathFetchTask != null) { - return getDirectoryPath(DATABASE_DIRECTORY); - } - // Context.getDatabasePath() returns path for the provided filename. - return appContext.getDatabasePath("foo").getParent(); + assert sDirPathFetchTask != null : "setDataDirectorySuffix must be called first."; + return getDirectoryPath(DATABASE_DIRECTORY); } /** @@ -112,10 +84,8 @@ @SuppressWarnings("unused") @CalledByNative public static String getCacheDirectory(Context appContext) { - if (sDirPathFetchTask != null) { - return getDirectoryPath(CACHE_DIRECTORY); - } - return appContext.getCacheDir().getPath(); + assert sDirPathFetchTask != null : "setDataDirectorySuffix must be called first."; + return getDirectoryPath(CACHE_DIRECTORY); } /**
diff --git a/base/android/java_runtime.cc b/base/android/java_runtime.cc new file mode 100644 index 0000000..5be9adfd --- /dev/null +++ b/base/android/java_runtime.cc
@@ -0,0 +1,25 @@ +// 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/android/java_runtime.h" + +#include "jni/Runtime_jni.h" + +namespace base { +namespace android { + +bool JavaRuntime::Register(JNIEnv* env) { + return JNI_Runtime::RegisterNativesImpl(env); +} + +void JavaRuntime::GetMemoryUsage(long* total_memory, long* free_memory) { + JNIEnv* env = base::android::AttachCurrentThread(); + base::android::ScopedJavaLocalRef<jobject> runtime = + JNI_Runtime::Java_Runtime_getRuntime(env); + *total_memory = JNI_Runtime::Java_Runtime_totalMemory(env, runtime.obj()); + *free_memory = JNI_Runtime::Java_Runtime_freeMemory(env, runtime.obj()); +} + +} // namespace android +} // namespace base
diff --git a/base/android/java_runtime.h b/base/android/java_runtime.h new file mode 100644 index 0000000..bde4c5c6b --- /dev/null +++ b/base/android/java_runtime.h
@@ -0,0 +1,28 @@ +// 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 BASE_ANDROID_JAVA_RUNTIME_H +#define BASE_ANDROID_JAVA_RUNTIME_H + +#include "base/android/scoped_java_ref.h" +#include "base/base_export.h" + +namespace base { +namespace android { + +// Wrapper class for using the java.lang.Runtime object from jni. +class BASE_EXPORT JavaRuntime { + public: + // Registers the jni class (once per process). + static bool Register(JNIEnv* env); + + // Fills the total memory used and memory allocated for objects by the java + // heap in the current process. Returns true on success. + static void GetMemoryUsage(long* total_memory, long* free_memory); +}; + +} // namespace android +} // namespace base + +#endif // BASE_ANDROID_JAVA_RUNTIME_H
diff --git a/base/base.gyp b/base/base.gyp index 4c066a6..325210f 100644 --- a/base/base.gyp +++ b/base/base.gyp
@@ -1390,9 +1390,22 @@ 'variables': { 'jni_gen_package': 'base', }, + 'dependencies': [ + 'android_runtime_jni_headers', + ], 'includes': [ '../build/jni_generator.gypi' ], }, { + # GN: //base:android_runtime_jni_headers + 'target_name': 'android_runtime_jni_headers', + 'type': 'none', + 'variables': { + 'jni_gen_package': 'base', + 'input_java_class': 'java/lang/Runtime.class', + }, + 'includes': [ '../build/jar_file_jni_generator.gypi' ], + }, + { # TODO(GN) 'target_name': 'base_unittests_jni_headers', 'type': 'none',
diff --git a/base/base.gypi b/base/base.gypi index 23f1c8a..a45a387 100644 --- a/base/base.gypi +++ b/base/base.gypi
@@ -45,6 +45,8 @@ 'android/important_file_writer_android.h', 'android/java_handler_thread.cc', 'android/java_handler_thread.h', + 'android/java_runtime.cc', + 'android/java_runtime.h', 'android/jni_android.cc', 'android/jni_android.h', 'android/jni_array.cc',
diff --git a/base/base_nacl.gyp b/base/base_nacl.gyp index 90a2893..40005d2 100644 --- a/base/base_nacl.gyp +++ b/base/base_nacl.gyp
@@ -39,9 +39,6 @@ '-fno-strict-aliasing', ], }, - 'dependencies': [ - '../native_client/tools.gyp:prep_toolchain', - ], }, { 'target_name': 'base_i18n_nacl', @@ -63,7 +60,6 @@ ], }, 'dependencies': [ - '../native_client/tools.gyp:prep_toolchain', '../third_party/icu/icu_nacl.gyp:icudata_nacl', '../third_party/icu/icu_nacl.gyp:icui18n_nacl', '../third_party/icu/icu_nacl.gyp:icuuc_nacl', @@ -113,7 +109,6 @@ 'rand_util_nacl.cc', ], 'dependencies': [ - '../native_client/tools.gyp:prep_toolchain', '../third_party/libevent/libevent_nacl_nonsfi.gyp:event_nacl_nonsfi', ], },
diff --git a/base/bind_unittest.cc b/base/bind_unittest.cc index a30b7756..f885403 100644 --- a/base/bind_unittest.cc +++ b/base/bind_unittest.cc
@@ -778,10 +778,10 @@ copies = 0; assigns = 0; - DerivedCopyCounter dervied(&copies, &assigns); + DerivedCopyCounter derived(&copies, &assigns); Callback<void(CopyCounter)> coerce_cb = Bind(&VoidPolymorphic1<CopyCounter>); - coerce_cb.Run(CopyCounter(dervied)); + coerce_cb.Run(CopyCounter(derived)); EXPECT_GE(2, copies); EXPECT_EQ(0, assigns); }
diff --git a/base/chromeos/memory_pressure_monitor_chromeos.h b/base/chromeos/memory_pressure_monitor_chromeos.h index b2a845d..45855ebc 100644 --- a/base/chromeos/memory_pressure_monitor_chromeos.h +++ b/base/chromeos/memory_pressure_monitor_chromeos.h
@@ -6,6 +6,7 @@ #define BASE_CHROMEOS_MEMORY_PRESSURE_MONITOR_CHROMEOS_H_ #include "base/base_export.h" +#include "base/files/scoped_file.h" #include "base/gtest_prod_util.h" #include "base/macros.h" #include "base/memory/memory_pressure_listener.h" @@ -13,10 +14,6 @@ #include "base/memory/weak_ptr.h" #include "base/timer/timer.h" -#if defined (OS_POSIX) -#include "base/files/scoped_file.h" -#endif - namespace base { class TestMemoryPressureMonitor; @@ -103,10 +100,8 @@ const int moderate_pressure_threshold_percent_; const int critical_pressure_threshold_percent_; -#if defined(OS_POSIX) // File descriptor used to detect low memory condition. ScopedFD low_mem_file_; -#endif base::WeakPtrFactory<MemoryPressureMonitorChromeOS> weak_ptr_factory_;
diff --git a/base/command_line.cc b/base/command_line.cc index 3e143cc5..61ff5c10 100644 --- a/base/command_line.cc +++ b/base/command_line.cc
@@ -263,7 +263,8 @@ } bool CommandLine::HasSwitch(const std::string& switch_string) const { - return switches_.find(LowerASCIIOnWindows(switch_string)) != switches_.end(); + DCHECK_EQ(StringToLowerASCII(switch_string), switch_string); + return switches_.find(switch_string) != switches_.end(); } bool CommandLine::HasSwitch(const char string_constant[]) const {
diff --git a/base/command_line.h b/base/command_line.h index 19df40c..439921e 100644 --- a/base/command_line.h +++ b/base/command_line.h
@@ -142,7 +142,7 @@ void SetProgram(const FilePath& program); // Returns true if this command line contains the given switch. - // (Switch names are case-insensitive). + // Switch names should only be lowercase. // The second override provides an optimized version to avoid inlining the // codegen for the string allocation. bool HasSwitch(const std::string& switch_string) const;
diff --git a/base/command_line_unittest.cc b/base/command_line_unittest.cc index e395c856..db1a0b2 100644 --- a/base/command_line_unittest.cc +++ b/base/command_line_unittest.cc
@@ -60,12 +60,13 @@ cl.GetProgram().value()); EXPECT_TRUE(cl.HasSwitch("foo")); - EXPECT_TRUE(cl.HasSwitch("bAr")); +#if defined(OS_WIN) + EXPECT_TRUE(cl.HasSwitch("bar")); +#else + EXPECT_FALSE(cl.HasSwitch("bar")); +#endif EXPECT_TRUE(cl.HasSwitch("baz")); EXPECT_TRUE(cl.HasSwitch("spaetzle")); -#if defined(OS_WIN) - EXPECT_TRUE(cl.HasSwitch("SPAETZLE")); -#endif EXPECT_TRUE(cl.HasSwitch("other-switches")); EXPECT_TRUE(cl.HasSwitch("input-translation")); @@ -128,7 +129,6 @@ EXPECT_TRUE(cl.HasSwitch("bar")); EXPECT_TRUE(cl.HasSwitch("baz")); EXPECT_TRUE(cl.HasSwitch("spaetzle")); - EXPECT_TRUE(cl.HasSwitch("SPAETZLE")); EXPECT_TRUE(cl.HasSwitch("other-switches")); EXPECT_TRUE(cl.HasSwitch("input-translation")); EXPECT_TRUE(cl.HasSwitch("quotes"));
diff --git a/base/files/file_path.h b/base/files/file_path.h index 93f9ec9..5225b12 100644 --- a/base/files/file_path.h +++ b/base/files/file_path.h
@@ -443,11 +443,9 @@ #if defined(OS_POSIX) #define FILE_PATH_LITERAL(x) x #define PRFilePath "s" -#define PRFilePathLiteral "%s" #elif defined(OS_WIN) #define FILE_PATH_LITERAL(x) L ## x #define PRFilePath "ls" -#define PRFilePathLiteral L"%ls" #endif // OS_WIN // Provide a hash function so that hash_sets and maps can contain FilePath
diff --git a/base/files/file_path_watcher_stub.cc b/base/files/file_path_watcher_stub.cc index d7ad2066..8138692e 100644 --- a/base/files/file_path_watcher_stub.cc +++ b/base/files/file_path_watcher_stub.cc
@@ -13,18 +13,18 @@ class FilePathWatcherImpl : public FilePathWatcher::PlatformDelegate { public: - virtual bool Watch(const FilePath& path, - bool recursive, - const FilePathWatcher::Callback& callback) override { + bool Watch(const FilePath& path, + bool recursive, + const FilePathWatcher::Callback& callback) override { return false; } - virtual void Cancel() override {} + void Cancel() override {} - virtual void CancelOnMessageLoopThread() override {} + void CancelOnMessageLoopThread() override {} protected: - virtual ~FilePathWatcherImpl() {} + ~FilePathWatcherImpl() override {} }; } // namespace
diff --git a/base/json/json_value_converter.h b/base/json/json_value_converter.h index c4bfe61..f94d46e3 100644 --- a/base/json/json_value_converter.h +++ b/base/json/json_value_converter.h
@@ -123,8 +123,7 @@ value_converter_(converter) { } - virtual bool ConvertField( - const base::Value& value, StructType* dst) const override { + bool ConvertField(const base::Value& value, StructType* dst) const override { return value_converter_->Convert(value, &(dst->*field_pointer_)); } @@ -202,8 +201,7 @@ ValueFieldConverter(ConvertFunc convert_func) : convert_func_(convert_func) {} - virtual bool Convert(const base::Value& value, - FieldType* field) const override { + bool Convert(const base::Value& value, FieldType* field) const override { return convert_func_(&value, field); } @@ -221,8 +219,7 @@ CustomFieldConverter(ConvertFunc convert_func) : convert_func_(convert_func) {} - virtual bool Convert(const base::Value& value, - FieldType* field) const override { + bool Convert(const base::Value& value, FieldType* field) const override { std::string string_value; return value.GetAsString(&string_value) && convert_func_(string_value, field); @@ -239,8 +236,7 @@ public: NestedValueConverter() {} - virtual bool Convert( - const base::Value& value, NestedType* field) const override { + bool Convert(const base::Value& value, NestedType* field) const override { return converter_.Convert(value, field); } @@ -254,8 +250,8 @@ public: RepeatedValueConverter() {} - virtual bool Convert( - const base::Value& value, ScopedVector<Element>* field) const override { + bool Convert(const base::Value& value, + ScopedVector<Element>* field) const override { const base::ListValue* list = NULL; if (!value.GetAsList(&list)) { // The field is not a list. @@ -290,8 +286,8 @@ public: RepeatedMessageConverter() {} - virtual bool Convert(const base::Value& value, - ScopedVector<NestedType>* field) const override { + bool Convert(const base::Value& value, + ScopedVector<NestedType>* field) const override { const base::ListValue* list = NULL; if (!value.GetAsList(&list)) return false; @@ -327,8 +323,8 @@ RepeatedCustomValueConverter(ConvertFunc convert_func) : convert_func_(convert_func) {} - virtual bool Convert(const base::Value& value, - ScopedVector<NestedType>* field) const override { + bool Convert(const base::Value& value, + ScopedVector<NestedType>* field) const override { const base::ListValue* list = NULL; if (!value.GetAsList(&list)) return false;
diff --git a/base/memory/discardable_memory.h b/base/memory/discardable_memory.h index 4138684..fc189e746 100644 --- a/base/memory/discardable_memory.h +++ b/base/memory/discardable_memory.h
@@ -56,8 +56,8 @@ virtual void* data() const = 0; // Handy method to simplify calling data() with a reinterpret_cast. - template<typename T> const T* data_as() const { - return reinterpret_cast<const T*>(data()); + template<typename T> T* data_as() const { + return reinterpret_cast<T*>(data()); } };
diff --git a/base/memory/ref_counted_delete_on_message_loop.h b/base/memory/ref_counted_delete_on_message_loop.h index 7b898ac..139a1ed 100644 --- a/base/memory/ref_counted_delete_on_message_loop.h +++ b/base/memory/ref_counted_delete_on_message_loop.h
@@ -8,7 +8,9 @@ #include "base/location.h" #include "base/logging.h" #include "base/memory/ref_counted.h" +// TODO(ricea): Remove the following include once all callers have been fixed. #include "base/message_loop/message_loop_proxy.h" +#include "base/single_thread_task_runner.h" namespace base { @@ -18,7 +20,7 @@ // Sample usage: // class Foo : public RefCountedDeleteOnMessageLoop<Foo> { // -// Foo(const scoped_refptr<MessageLoopProxy>& loop) +// Foo(const scoped_refptr<SingleThreadTaskRunner>& loop) // : RefCountedDeleteOnMessageLoop<Foo>(loop) { // ... // } @@ -33,9 +35,14 @@ template <class T> class RefCountedDeleteOnMessageLoop : public subtle::RefCountedThreadSafeBase { public: + // This constructor will accept a MessageL00pProxy object, but new code should + // prefer a SingleThreadTaskRunner. A SingleThreadTaskRunner for the + // MessageLoop on the current thread can be acquired by calling + // MessageLoop::current()->task_runner(). RefCountedDeleteOnMessageLoop( - const scoped_refptr<MessageLoopProxy>& proxy) : proxy_(proxy) { - DCHECK(proxy_.get()); + const scoped_refptr<SingleThreadTaskRunner>& task_runner) + : task_runner_(task_runner) { + DCHECK(task_runner_.get()); } void AddRef() const { @@ -53,13 +60,13 @@ void DestructOnMessageLoop() const { const T* t = static_cast<const T*>(this); - if (proxy_->BelongsToCurrentThread()) + if (task_runner_->BelongsToCurrentThread()) delete t; else - proxy_->DeleteSoon(FROM_HERE, t); + task_runner_->DeleteSoon(FROM_HERE, t); } - scoped_refptr<MessageLoopProxy> proxy_; + scoped_refptr<SingleThreadTaskRunner> task_runner_; private: DISALLOW_COPY_AND_ASSIGN(RefCountedDeleteOnMessageLoop);
diff --git a/base/observer_list_unittest.cc b/base/observer_list_unittest.cc index 636aa83..46b350b 100644 --- a/base/observer_list_unittest.cc +++ b/base/observer_list_unittest.cc
@@ -71,7 +71,7 @@ adder(1) { } - virtual void Observe(int x) override { + void Observe(int x) override { if (!added) { added = true; observer_list->AddObserver(&adder);
diff --git a/base/prefs/pref_member.h b/base/prefs/pref_member.h index 078be95..9b140d1 100644 --- a/base/prefs/pref_member.h +++ b/base/prefs/pref_member.h
@@ -264,9 +264,9 @@ } protected: - virtual ~Internal() {} + ~Internal() override {} - virtual BASE_PREFS_EXPORT bool UpdateValueInternal( + BASE_PREFS_EXPORT bool UpdateValueInternal( const base::Value& value) const override; // We cache the value of the pref so we don't have to keep walking the pref @@ -277,8 +277,8 @@ DISALLOW_COPY_AND_ASSIGN(Internal); }; - virtual Internal* internal() const override { return internal_.get(); } - virtual void CreateInternal() const override { internal_ = new Internal(); } + Internal* internal() const override { return internal_.get(); } + void CreateInternal() const override { internal_ = new Internal(); } // This method is used to do the actual sync with pref of the specified type. void BASE_PREFS_EXPORT UpdatePref(const ValueType& value);
diff --git a/base/process/memory_unittest.cc b/base/process/memory_unittest.cc index 29e76dd..0276b49 100644 --- a/base/process/memory_unittest.cc +++ b/base/process/memory_unittest.cc
@@ -128,10 +128,11 @@ // Android doesn't implement set_new_handler, so we can't use the // OutOfMemoryTest cases. OpenBSD does not support these tests either. -// Don't test these on ASAN configurations: only test the real allocator. +// Don't test these on ASan/TSan/MSan configurations: only test the real +// allocator. // TODO(vandebo) make this work on Windows too. #if !defined(OS_ANDROID) && !defined(OS_OPENBSD) && !defined(OS_WIN) && \ - !defined(ADDRESS_SANITIZER) + !defined(MEMORY_TOOL_REPLACES_ALLOCATOR) #if defined(USE_TCMALLOC) extern "C" {
diff --git a/base/security_unittest.cc b/base/security_unittest.cc index 5003269..07ba6f5a 100644 --- a/base/security_unittest.cc +++ b/base/security_unittest.cc
@@ -69,7 +69,7 @@ // We also use it so that the compiler doesn't discard certain return values // as something we don't need (see the comment with calloc below). template <typename Type> -Type HideValueFromCompiler(volatile Type value) { +NOINLINE Type HideValueFromCompiler(volatile Type value) { #if defined(__GNUC__) // In a GCC compatible compiler (GCC or Clang), make this compiler barrier // more robust than merely using "volatile". @@ -290,11 +290,11 @@ // Call calloc(), eventually free the memory and return whether or not // calloc() did succeed. bool CallocReturnsNull(size_t nmemb, size_t size) { - // We need the two calls to HideValueFromCompiler(): we have seen LLVM + scoped_ptr<char, base::FreeDeleter> array_pointer( + static_cast<char*>(calloc(nmemb, size))); + // We need the call to HideValueFromCompiler(): we have seen LLVM // optimize away the call to calloc() entirely and assume the pointer to not // be NULL. - scoped_ptr<char, base::FreeDeleter> array_pointer( - static_cast<char*>(HideValueFromCompiler(calloc(nmemb, size)))); return HideValueFromCompiler(array_pointer.get()) == NULL; }
diff --git a/base/test/gtest_xml_util.h b/base/test/gtest_xml_util.h index f832cde..9ff2406 100644 --- a/base/test/gtest_xml_util.h +++ b/base/test/gtest_xml_util.h
@@ -21,17 +21,17 @@ class XmlUnitTestResultPrinter : public testing::EmptyTestEventListener { public: XmlUnitTestResultPrinter(); - virtual ~XmlUnitTestResultPrinter(); + ~XmlUnitTestResultPrinter() override; // Must be called before adding as a listener. Returns true on success. bool Initialize(const FilePath& output_file_path) WARN_UNUSED_RESULT; private: // testing::EmptyTestEventListener: - virtual void OnTestCaseStart(const testing::TestCase& test_case) override; - virtual void OnTestStart(const testing::TestInfo& test_info) override; - virtual void OnTestEnd(const testing::TestInfo& test_info) override; - virtual void OnTestCaseEnd(const testing::TestCase& test_case) override; + void OnTestCaseStart(const testing::TestCase& test_case) override; + void OnTestStart(const testing::TestInfo& test_info) override; + void OnTestEnd(const testing::TestInfo& test_info) override; + void OnTestCaseEnd(const testing::TestCase& test_case) override; FILE* output_file_;
diff --git a/base/test/test_suite.cc b/base/test/test_suite.cc index d40dd983..ee135e53 100644 --- a/base/test/test_suite.cc +++ b/base/test/test_suite.cc
@@ -55,7 +55,7 @@ class MaybeTestDisabler : public testing::EmptyTestEventListener { public: - virtual void OnTestStart(const testing::TestInfo& test_info) override { + void OnTestStart(const testing::TestInfo& test_info) override { ASSERT_FALSE(TestSuite::IsMarkedMaybe(test_info)) << "Probably the OS #ifdefs don't include all of the necessary " "platforms.\nPlease ensure that no tests have the MAYBE_ prefix " @@ -69,11 +69,11 @@ : old_command_line_(base::CommandLine::NO_PROGRAM) { } - virtual void OnTestStart(const testing::TestInfo& test_info) override { + void OnTestStart(const testing::TestInfo& test_info) override { old_command_line_ = *base::CommandLine::ForCurrentProcess(); } - virtual void OnTestEnd(const testing::TestInfo& test_info) override { + void OnTestEnd(const testing::TestInfo& test_info) override { *base::CommandLine::ForCurrentProcess() = old_command_line_; }
diff --git a/base/third_party/xdg_mime/README.chromium b/base/third_party/xdg_mime/README.chromium index 29d239a..95f3a965 100644 --- a/base/third_party/xdg_mime/README.chromium +++ b/base/third_party/xdg_mime/README.chromium
@@ -7,5 +7,7 @@ @ 2cdd8d36d7930d5a594587286cb1949ff62f7027 on 2012/08/06. In addition, we have the following patch(es): -- compile.patch: small tweaks to make the code compile. -- Added a LICENSE file. + - compile.patch: small tweaks to make the code compile. + - free_pointer_later.patch: small patch that fixes potential crash in + xdg_mime_get_mime_type_for_file() - use of pointer after being freed. + - Added a LICENSE file.
diff --git a/base/third_party/xdg_mime/free_pointer_later.patch b/base/third_party/xdg_mime/free_pointer_later.patch new file mode 100644 index 0000000..7668761 --- /dev/null +++ b/base/third_party/xdg_mime/free_pointer_later.patch
@@ -0,0 +1,22 @@ +diff --git a/base/third_party/xdg_mime/xdgmime.c b/base/third_party/xdg_mime/xdgmime.c +index c7b16bb..6dc58c2 100644 +--- a/base/third_party/xdg_mime/xdgmime.c ++++ b/base/third_party/xdg_mime/xdgmime.c +@@ -558,13 +558,13 @@ xdg_mime_get_mime_type_for_file (const char *file_name, + mime_type = _xdg_mime_magic_lookup_data (global_magic, data, bytes_read, NULL, + mime_types, n); + +- free (data); + fclose (file); + +- if (mime_type) +- return mime_type; ++ if (!mime_type) ++ mime_type = _xdg_binary_or_text_fallback(data, bytes_read); + +- return _xdg_binary_or_text_fallback(data, bytes_read); ++ free (data); ++ return mime_type; + } + + const char *
diff --git a/base/third_party/xdg_mime/xdgmime.c b/base/third_party/xdg_mime/xdgmime.c index c7b16bbc..6dc58c2 100644 --- a/base/third_party/xdg_mime/xdgmime.c +++ b/base/third_party/xdg_mime/xdgmime.c
@@ -558,13 +558,13 @@ mime_type = _xdg_mime_magic_lookup_data (global_magic, data, bytes_read, NULL, mime_types, n); - free (data); fclose (file); - if (mime_type) - return mime_type; + if (!mime_type) + mime_type = _xdg_binary_or_text_fallback(data, bytes_read); - return _xdg_binary_or_text_fallback(data, bytes_read); + free (data); + return mime_type; } const char *
diff --git a/base/threading/thread_perftest.cc b/base/threading/thread_perftest.cc index b94f942..a08cc5bf 100644 --- a/base/threading/thread_perftest.cc +++ b/base/threading/thread_perftest.cc
@@ -174,12 +174,12 @@ template <typename WaitableEventType> class EventPerfTest : public ThreadPerfTest { public: - virtual void Init() override { + void Init() override { for (size_t i = 0; i < threads_.size(); i++) events_.push_back(new WaitableEventType(false, false)); } - virtual void Reset() override { events_.clear(); } + void Reset() override { events_.clear(); } void WaitAndSignalOnThread(size_t event) { size_t next_event = (event + 1) % events_.size(); @@ -195,7 +195,7 @@ FinishMeasurement(); } - virtual void PingPong(int hops) override { + void PingPong(int hops) override { remaining_hops_ = hops; for (size_t i = 0; i < threads_.size(); i++) { threads_[i]->message_loop_proxy()->PostTask(
diff --git a/base/trace_event/malloc_dump_provider.cc b/base/trace_event/malloc_dump_provider.cc index 94ef5ed..7d9931c 100644 --- a/base/trace_event/malloc_dump_provider.cc +++ b/base/trace_event/malloc_dump_provider.cc
@@ -33,7 +33,6 @@ // the current process. bool MallocDumpProvider::DumpInto(ProcessMemoryDump* pmd) { struct mallinfo info = mallinfo(); - DCHECK(info.uordblks > 0); DCHECK_GE(info.arena + info.hblkhd, info.uordblks); MemoryAllocatorDump* dump =
diff --git a/base/trace_event/memory_dump_manager.cc b/base/trace_event/memory_dump_manager.cc index 31fe92e..63d91bb4 100644 --- a/base/trace_event/memory_dump_manager.cc +++ b/base/trace_event/memory_dump_manager.cc
@@ -13,6 +13,12 @@ #include "base/trace_event/process_memory_dump.h" #include "base/trace_event/trace_event_argument.h" +#if defined(OS_LINUX) || defined(OS_ANDROID) +#include "base/trace_event/malloc_dump_provider.h" +#include "base/trace_event/process_memory_maps_dump_provider.h" +#include "base/trace_event/process_memory_totals_dump_provider.h" +#endif + namespace base { namespace trace_event { @@ -20,9 +26,10 @@ // TODO(primiano): this should be smarter and should do something similar to // trace event synthetic delays. -const char kTraceCategory[] = TRACE_DISABLED_BY_DEFAULT("memory-dumps"); +const char kTraceCategory[] = TRACE_DISABLED_BY_DEFAULT("memory-infra"); MemoryDumpManager* g_instance_for_testing = nullptr; +const int kDumpIntervalSeconds = 2; const int kTraceEventNumArgs = 1; const char* kTraceEventArgNames[] = {"dumps"}; const unsigned char kTraceEventArgTypes[] = {TRACE_VALUE_TYPE_CONVERTABLE}; @@ -108,6 +115,11 @@ } } +void RequestPeriodicGlobalDump() { + MemoryDumpManager::GetInstance()->RequestGlobalDump( + MemoryDumpType::PERIODIC_INTERVAL); +} + } // namespace // static @@ -124,13 +136,16 @@ // static void MemoryDumpManager::SetInstanceForTesting(MemoryDumpManager* instance) { + if (instance) + instance->skip_core_dumpers_auto_registration_for_testing_ = true; g_instance_for_testing = instance; } MemoryDumpManager::MemoryDumpManager() : dump_provider_currently_active_(nullptr), delegate_(nullptr), - memory_tracing_enabled_(0) { + memory_tracing_enabled_(0), + skip_core_dumpers_auto_registration_for_testing_(false) { g_next_guid.GetNext(); // Make sure that first guid is not zero. } @@ -141,6 +156,16 @@ void MemoryDumpManager::Initialize() { TRACE_EVENT0(kTraceCategory, "init"); // Add to trace-viewer category list. trace_event::TraceLog::GetInstance()->AddEnabledStateObserver(this); + + if (skip_core_dumpers_auto_registration_for_testing_) + return; + +#if defined(OS_LINUX) || defined(OS_ANDROID) + // Enable the core dump providers. + RegisterDumpProvider(ProcessMemoryTotalsDumpProvider::GetInstance()); + RegisterDumpProvider(ProcessMemoryMapsDumpProvider::GetInstance()); + RegisterDumpProvider(MallocDumpProvider::GetInstance()); +#endif } void MemoryDumpManager::SetDelegate(MemoryDumpManagerDelegate* delegate) { @@ -309,22 +334,33 @@ TRACE_EVENT_CATEGORY_GROUP_ENABLED(kTraceCategory, &enabled); AutoLock lock(lock_); - dump_providers_enabled_.clear(); - if (enabled) { - // Merge the dictionary of allocator attributes from all dump providers - // into the session state. - session_state_ = new MemoryDumpSessionState(); - for (const MemoryDumpProvider* mdp : dump_providers_registered_) { - session_state_->allocators_attributes_type_info.Update( - mdp->allocator_attributes_type_info()); - } - dump_providers_enabled_ = dump_providers_registered_; + + // There is no point starting the tracing without a delegate. + if (!enabled || !delegate_) { + dump_providers_enabled_.clear(); + return; } + + // Merge the dictionary of allocator attributes from all dump providers + // into the session state. + session_state_ = new MemoryDumpSessionState(); + for (const MemoryDumpProvider* mdp : dump_providers_registered_) { + session_state_->allocators_attributes_type_info.Update( + mdp->allocator_attributes_type_info()); + } + dump_providers_enabled_ = dump_providers_registered_; subtle::NoBarrier_Store(&memory_tracing_enabled_, 1); + + if (delegate_->IsCoordinatorProcess()) { + periodic_dump_timer_.Start(FROM_HERE, + TimeDelta::FromSeconds(kDumpIntervalSeconds), + base::Bind(&RequestPeriodicGlobalDump)); + } } void MemoryDumpManager::OnTraceLogDisabled() { AutoLock lock(lock_); + periodic_dump_timer_.Stop(); dump_providers_enabled_.clear(); subtle::NoBarrier_Store(&memory_tracing_enabled_, 0); session_state_ = nullptr;
diff --git a/base/trace_event/memory_dump_manager.h b/base/trace_event/memory_dump_manager.h index 6fc2341e..371a47a 100644 --- a/base/trace_event/memory_dump_manager.h +++ b/base/trace_event/memory_dump_manager.h
@@ -12,6 +12,7 @@ #include "base/memory/ref_counted.h" #include "base/memory/singleton.h" #include "base/synchronization/lock.h" +#include "base/timer/timer.h" #include "base/trace_event/memory_dump_request_args.h" #include "base/trace_event/trace_event.h" @@ -120,6 +121,12 @@ // dump_providers_enabled_ list) when tracing is not enabled. subtle::AtomicWord memory_tracing_enabled_; + // For time-triggered periodic dumps. + RepeatingTimer<MemoryDumpManager> periodic_dump_timer_; + + // Skips the auto-registration of the core dumpers during Initialize(). + bool skip_core_dumpers_auto_registration_for_testing_; + DISALLOW_COPY_AND_ASSIGN(MemoryDumpManager); }; @@ -130,6 +137,10 @@ virtual void RequestGlobalMemoryDump(const MemoryDumpRequestArgs& args, const MemoryDumpCallback& callback) = 0; + // Determines whether the MemoryDumpManager instance should be the master + // (the ones which initiates and coordinates the multiprocess dumps) or not. + virtual bool IsCoordinatorProcess() const = 0; + protected: MemoryDumpManagerDelegate() {} virtual ~MemoryDumpManagerDelegate() {}
diff --git a/base/trace_event/memory_dump_manager_unittest.cc b/base/trace_event/memory_dump_manager_unittest.cc index 0de60b29..589c4061 100644 --- a/base/trace_event/memory_dump_manager_unittest.cc +++ b/base/trace_event/memory_dump_manager_unittest.cc
@@ -30,6 +30,8 @@ const MemoryDumpCallback& callback) override { CreateProcessDump(args, callback); } + + bool IsCoordinatorProcess() const override { return false; } }; class MemoryDumpManagerTest : public testing::Test {
diff --git a/base/trace_event/trace_event.h b/base/trace_event/trace_event.h index 7c4d3ab..e0249f5 100644 --- a/base/trace_event/trace_event.h +++ b/base/trace_event/trace_event.h
@@ -144,11 +144,11 @@ // class MyData : public base::trace_event::ConvertableToTraceFormat { // public: // MyData() {} -// virtual void AppendAsTraceFormat(std::string* out) const override { +// void AppendAsTraceFormat(std::string* out) const override { // out->append("{\"foo\":1}"); // } // private: -// virtual ~MyData() {} +// ~MyData() override {} // DISALLOW_COPY_AND_ASSIGN(MyData); // }; // @@ -601,6 +601,12 @@ TRACE_EVENT_PHASE_ASYNC_BEGIN, category_group, name, id, \ static_cast<int>(base::PlatformThread::CurrentId()), \ timestamp, TRACE_EVENT_FLAG_NONE) +#define TRACE_EVENT_COPY_ASYNC_BEGIN_WITH_TIMESTAMP0(category_group, \ + name, id, timestamp) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP( \ + TRACE_EVENT_PHASE_ASYNC_BEGIN, category_group, name, id, \ + static_cast<int>(base::PlatformThread::CurrentId()), \ + timestamp, TRACE_EVENT_FLAG_COPY) // Records a single ASYNC_STEP_INTO event for |step| immediately. If the // category is not enabled, then this does nothing. The |name| and |id| must
diff --git a/base/win/event_trace_consumer.h b/base/win/event_trace_consumer.h index 9322e1e..fd448949 100644 --- a/base/win/event_trace_consumer.h +++ b/base/win/event_trace_consumer.h
@@ -119,7 +119,7 @@ template <class ImplClass> inline HRESULT EtwTraceConsumerBase<ImplClass>::Consume() { ULONG err = ::ProcessTrace(&trace_handles_[0], - trace_handles_.size(), + static_cast<ULONG>(trace_handles_.size()), NULL, NULL); return HRESULT_FROM_WIN32(err);
diff --git a/base/win/scoped_handle.cc b/base/win/scoped_handle.cc index 2ebef32..33a8aa5c 100644 --- a/base/win/scoped_handle.cc +++ b/base/win/scoped_handle.cc
@@ -132,6 +132,10 @@ // This lock only protects against races in this module, which is fine. AutoNativeLock lock(g_lock.Get()); g_active_verifier = verifier ? verifier : new ActiveVerifier(true); + + // TODO(shrikant): Enable handle verifier after figuring out + // AppContainer/DuplicateHandle error. + g_active_verifier->Disable(); #endif }
diff --git a/breakpad/breakpad.gyp b/breakpad/breakpad.gyp index cc5187d..545456ee 100644 --- a/breakpad/breakpad.gyp +++ b/breakpad/breakpad.gyp
@@ -846,6 +846,11 @@ 'src/client/mac/Framework', 'src/common/mac', ], + 'direct_dependent_settings': { + 'include_dirs': [ + 'src', + ], + }, } ] }],
diff --git a/breakpad/breakpad_handler.gypi b/breakpad/breakpad_handler.gypi index 540e87e..34b448d 100644 --- a/breakpad/breakpad_handler.gypi +++ b/breakpad/breakpad_handler.gypi
@@ -39,6 +39,7 @@ ['OS=="win"', { 'targets': [ { + # GN version: //breakpad:breakpad_handler 'target_name': 'breakpad_handler', 'type': 'static_library', 'variables': {
diff --git a/build/PRESUBMIT.py b/build/PRESUBMIT.py index 3c70193..fca962f 100644 --- a/build/PRESUBMIT.py +++ b/build/PRESUBMIT.py
@@ -2,8 +2,6 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -WHITELIST = [ r'.+_test.py$' ] - def _RunTests(input_api, output_api): return (input_api.canned_checks.RunUnitTestsInDirectory(
diff --git a/build/all.gyp b/build/all.gyp index 8f6295b..255a35c 100644 --- a/build/all.gyp +++ b/build/all.gyp
@@ -495,7 +495,7 @@ ], 'conditions': [ # TODO(GYP): make gn_migration.gypi work unconditionally. - ['OS=="win" or (OS=="linux" and target_arch=="x64" and chromecast==0)', { + ['OS=="mac" or OS=="win" or (OS=="linux" and target_arch=="x64" and chromecast==0)', { 'includes': [ 'gn_migration.gypi', ], @@ -833,6 +833,7 @@ '../base/base.gyp:base_unittests_apk', '../cc/blink/cc_blink_tests.gyp:cc_blink_unittests_apk', '../cc/cc_tests.gyp:cc_unittests_apk', + '../components/components_tests.gyp:components_browsertests_apk', '../components/components_tests.gyp:components_unittests_apk', '../content/content_shell_and_tests.gyp:content_browsertests_apk', '../content/content_shell_and_tests.gyp:content_gl_tests_apk',
diff --git a/build/android/gyp/util/build_device.py b/build/android/gyp/util/build_device.py index b153a15..7e0d57b 100644 --- a/build/android/gyp/util/build_device.py +++ b/build/android/gyp/util/build_device.py
@@ -40,7 +40,7 @@ return self.id def Install(self, *args, **kwargs): - return self.device.old_interface.Install(*args, **kwargs) + return self.device.Install(*args, **kwargs) def GetInstallMetadata(self, apk_package): """Gets the metadata on the device for the apk_package apk."""
diff --git a/build/android/gyp/write_build_config.py b/build/android/gyp/write_build_config.py index 5fc8955..d8f2d74 100755 --- a/build/android/gyp/write_build_config.py +++ b/build/android/gyp/write_build_config.py
@@ -88,6 +88,10 @@ parser.add_option('--native-libs', help='List of top-level native libs.') parser.add_option('--readelf-path', help='Path to toolchain\'s readelf.') + parser.add_option('--tested-apk-config', + help='Path to the build config of the tested apk (for an instrumentation ' + 'test apk).') + options, args = parser.parse_args(argv) if args: @@ -217,13 +221,26 @@ c['package_name'] for c in all_resources_deps if 'package_name' in c] + deps_dex_files = [c['dex_path'] for c in all_library_deps] + # An instrumentation test apk should exclude the dex files that are in the apk + # under test. + if options.type == 'android_apk' and options.tested_apk_config: + tested_apk_config_paths = GetAllDepsConfigsInOrder( + [options.tested_apk_config]) + tested_apk_configs = [GetDepConfig(p) for p in tested_apk_config_paths] + tested_apk_library_deps = DepsOfType('java_library', tested_apk_configs) + tested_apk_deps_dex_files = [c['dex_path'] for c in tested_apk_library_deps] + deps_dex_files = [ + p for p in deps_dex_files if not p in tested_apk_deps_dex_files] + + # Dependencies for the final dex file of an apk or a 'deps_dex'. if options.type in ['android_apk', 'deps_dex']: config['final_dex'] = {} dex_config = config['final_dex'] # TODO(cjhopman): proguard version - dex_deps_files = [c['dex_path'] for c in all_library_deps] - dex_config['dependency_dex_files'] = dex_deps_files + dex_config['dependency_dex_files'] = deps_dex_files + if options.type == 'android_apk': config['dist_jar'] = {
diff --git a/build/android/pylib/constants.py b/build/android/pylib/constants/__init__.py similarity index 95% rename from build/android/pylib/constants.py rename to build/android/pylib/constants/__init__.py index 8f8e782..6e92f6d 100644 --- a/build/android/pylib/constants.py +++ b/build/android/pylib/constants/__init__.py
@@ -3,6 +3,9 @@ # found in the LICENSE file. """Defines a set of constants shared by test runners and other scripts.""" + +# TODO(jbudorick): Split these constants into coherent modules. + # pylint: disable=W0212 import collections @@ -13,7 +16,7 @@ DIR_SOURCE_ROOT = os.environ.get('CHECKOUT_SOURCE_ROOT', os.path.abspath(os.path.join(os.path.dirname(__file__), - os.pardir, os.pardir, os.pardir))) + os.pardir, os.pardir, os.pardir, os.pardir))) ISOLATE_DEPS_DIR = os.path.join(DIR_SOURCE_ROOT, 'isolate_deps_dir') CHROME_SHELL_HOST_DRIVEN_DIR = os.path.join( @@ -103,6 +106,13 @@ '/data/local/tmp/chrome-native-tests-command-line', None, None), + 'components_browsertests': PackageInfo( + 'org.chromium.components_browsertests_apk', + ('org.chromium.components_browsertests_apk' + + '.ComponentsBrowserTestsActivity'), + '/data/local/tmp/components-browser-tests-command-line', + None, + None), 'content_browsertests': PackageInfo( 'org.chromium.content_browsertests_apk', 'org.chromium.content_browsertests_apk.ContentBrowserTestsActivity',
diff --git a/build/android/pylib/constants/keyevent.py b/build/android/pylib/constants/keyevent.py new file mode 100644 index 0000000..06736b3d --- /dev/null +++ b/build/android/pylib/constants/keyevent.py
@@ -0,0 +1,14 @@ +# 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. + +"""Android KeyEvent constants. + +http://developer.android.com/reference/android/view/KeyEvent.html +""" + +KEYCODE_BACK = 4 +KEYCODE_DPAD_RIGHT = 22 +KEYCODE_ENTER = 66 +KEYCODE_MENU = 82 +
diff --git a/build/android/pylib/forwarder.py b/build/android/pylib/forwarder.py index eb83d68..d16b9b1 100644 --- a/build/android/pylib/forwarder.py +++ b/build/android/pylib/forwarder.py
@@ -288,11 +288,8 @@ self._device_forwarder_path_on_host, Forwarder._DEVICE_FORWARDER_FOLDER)]) cmd = '%s %s' % (tool.GetUtilWrapper(), Forwarder._DEVICE_FORWARDER_PATH) - (exit_code, output) = device.old_interface.GetAndroidToolStatusAndOutput( - cmd, lib_path=Forwarder._DEVICE_FORWARDER_FOLDER) - if exit_code != 0: - raise Exception( - 'Failed to start device forwarder:\n%s' % '\n'.join(output)) + device.RunShellCommand( + cmd, env={'LD_LIBRARY_PATH': Forwarder._DEVICE_FORWARDER_FOLDER}) self._initialized_devices.add(device_serial) def _KillHostLocked(self): @@ -328,5 +325,5 @@ cmd = '%s %s --kill-server' % (tool.GetUtilWrapper(), Forwarder._DEVICE_FORWARDER_PATH) - device.old_interface.GetAndroidToolStatusAndOutput( - cmd, lib_path=Forwarder._DEVICE_FORWARDER_FOLDER) + device.RunShellCommand( + cmd, env={'LD_LIBRARY_PATH': Forwarder._DEVICE_FORWARDER_FOLDER})
diff --git a/build/android/pylib/gtest/gtest_config.py b/build/android/pylib/gtest/gtest_config.py index 8608ed70..6ce0fb1 100644 --- a/build/android/pylib/gtest/gtest_config.py +++ b/build/android/pylib/gtest/gtest_config.py
@@ -6,6 +6,7 @@ # Add new suites here before upgrading them to the stable list below. EXPERIMENTAL_TEST_SUITES = [ + 'components_browsertests', 'content_gl_tests', 'heap_profiler_unittests', 'devtools_bridge_tests',
diff --git a/build/android/pylib/gtest/gtest_test_instance.py b/build/android/pylib/gtest/gtest_test_instance.py index 186b8fc..b6f83b31 100644 --- a/build/android/pylib/gtest/gtest_test_instance.py +++ b/build/android/pylib/gtest/gtest_test_instance.py
@@ -92,9 +92,10 @@ raise ValueError('Platform mode currently supports only 1 gtest suite') self._suite = args.suite_name[0] - if self._suite == 'content_browsertests': - error_func('content_browsertests are not currently supported ' - 'in platform mode.') + if (self._suite == 'content_browsertests' or + self._suite == 'components_browsertests'): + error_func('%s are not currently supported ' + 'in platform mode.' % self._suite) self._apk_path = os.path.join( constants.GetOutDirectory(), 'apks', '%s.apk' % self._suite) else:
diff --git a/build/android/pylib/gtest/local_device_gtest_run.py b/build/android/pylib/gtest/local_device_gtest_run.py index ff12d82a..fd143d6 100644 --- a/build/android/pylib/gtest/local_device_gtest_run.py +++ b/build/android/pylib/gtest/local_device_gtest_run.py
@@ -30,7 +30,8 @@ # TODO(jbudorick): Move this up to the test instance if the net test server is # handled outside of the APK for the remote_device environment. _SUITE_REQUIRES_TEST_SERVER_SPAWNER = [ - 'content_unittests', 'content_browsertests', 'net_unittests', 'unit_tests' + 'components_browsertests', 'content_unittests', 'content_browsertests', + 'net_unittests', 'unit_tests' ] class _ApkDelegate(object):
diff --git a/build/android/pylib/gtest/setup.py b/build/android/pylib/gtest/setup.py index 1b882ca..44662d08 100644 --- a/build/android/pylib/gtest/setup.py +++ b/build/android/pylib/gtest/setup.py
@@ -31,6 +31,7 @@ 'third_party/WebKit/Source/platform/heap/BlinkHeapUnitTests.isolate', 'breakpad_unittests': 'breakpad/breakpad_unittests.isolate', 'cc_perftests': 'cc/cc_perftests.isolate', + 'components_browsertests': 'components/components_browsertests.isolate', 'components_unittests': 'components/components_unittests.isolate', 'content_browsertests': 'content/content_browsertests.isolate', 'content_unittests': 'content/content_unittests.isolate', @@ -236,7 +237,8 @@ tests = unittest_util.FilterTestNames(tests, test_options.gtest_filter) # Coalesce unit tests into a single test per device - if test_options.suite_name != 'content_browsertests': + if (test_options.suite_name != 'content_browsertests' and + test_options.suite_name != 'components_browsertests'): num_devices = len(devices) tests = [':'.join(tests[i::num_devices]) for i in xrange(num_devices)] tests = [t for t in tests if t]
diff --git a/build/android/pylib/gtest/test_package_apk.py b/build/android/pylib/gtest/test_package_apk.py index 3e579d2..8da3c74 100644 --- a/build/android/pylib/gtest/test_package_apk.py +++ b/build/android/pylib/gtest/test_package_apk.py
@@ -34,6 +34,10 @@ self.suite_path = os.path.join( constants.GetOutDirectory(), 'apks', '%s.apk' % suite_name) self._package_info = constants.PACKAGE_INFO['content_browsertests'] + elif suite_name == 'components_browsertests': + self.suite_path = os.path.join( + constants.GetOutDirectory(), 'apks', '%s.apk' % suite_name) + self._package_info = constants.PACKAGE_INFO['components_browsertests'] else: self.suite_path = os.path.join( constants.GetOutDirectory(), '%s_apk' % suite_name, @@ -90,6 +94,15 @@ # TODO(jbudorick) Handle this exception appropriately once the # conversions are done. pass + elif self.suite_name == 'components_browsertests': + try: + device.RunShellCommand( + 'rm -r %s/components_shell' % device.GetExternalStoragePath(), + timeout=60 * 2) + except device_errors.CommandFailedError: + # TODO(jbudorick) Handle this exception appropriately once the + # conversions are done. + pass #override def CreateCommandLineFileOnDevice(self, device, test_filter, test_arguments):
diff --git a/build/android/pylib/gtest/test_runner.py b/build/android/pylib/gtest/test_runner.py index 0fc6c12..49263888 100644 --- a/build/android/pylib/gtest/test_runner.py +++ b/build/android/pylib/gtest/test_runner.py
@@ -26,10 +26,15 @@ # to output the CRASHED marker when a crash happens. RE_CRASH = re.compile('\\[ CRASHED \\](.*)\r\n') +# Bots that don't output anything for 20 minutes get timed out, so that's our +# hard cap. +_INFRA_STDOUT_TIMEOUT = 20 * 60 + def _TestSuiteRequiresMockTestServer(suite_name): """Returns True if the test suite requires mock test server.""" tests_require_net_test_server = ['unit_tests', 'net_unittests', + 'components_browsertests', 'content_unittests', 'content_browsertests'] return (suite_name in @@ -62,7 +67,8 @@ if os.environ.get('BUILDBOT_SLAVENAME'): timeout = timeout * 2 - self._timeout = timeout * self.tool.GetTimeoutScale() + self._timeout = min(timeout * self.tool.GetTimeoutScale(), + _INFRA_STDOUT_TIMEOUT) if _TestSuiteRequiresHighPerfMode(self.test_package.suite_name): self._perf_controller = perf_control.PerfControl(self.device)
diff --git a/build/android/pylib/local/device/local_device_instrumentation_test_run.py b/build/android/pylib/local/device/local_device_instrumentation_test_run.py index ac3f5b1..1dc3ed4 100644 --- a/build/android/pylib/local/device/local_device_instrumentation_test_run.py +++ b/build/android/pylib/local/device/local_device_instrumentation_test_run.py
@@ -3,11 +3,14 @@ # found in the LICENSE file. import logging +import re import time from pylib import flag_changer from pylib.base import base_test_result from pylib.base import test_run +from pylib.constants import keyevent +from pylib.device import device_errors from pylib.local.device import local_device_test_run @@ -27,16 +30,36 @@ def DidPackageCrashOnDevice(package_name, device): # Dismiss any error dialogs. Limit the number in case we have an error # loop or we are failing to dismiss. - for _ in xrange(10): - package = device.old_interface.DismissCrashDialogIfNeeded() - if not package: - return False - # Assume test package convention of ".test" suffix - if package in package_name: - return True + try: + for _ in xrange(10): + package = _DismissCrashDialog(device) + if not package: + return False + # Assume test package convention of ".test" suffix + if package in package_name: + return True + except device_errors.CommandFailedError: + logging.exception('Error while attempting to dismiss crash dialog.') return False +_CURRENT_FOCUS_CRASH_RE = re.compile( + r'\s*mCurrentFocus.*Application (Error|Not Responding): (\S+)}') + + +def _DismissCrashDialog(device): + for l in device.RunShellCommand( + ['dumpsys', 'window', 'windows'], check_return=True): + m = re.match(_CURRENT_FOCUS_CRASH_RE, l) + if m: + device.SendKeyEvent(keyevent.KEYCODE_DPAD_RIGHT) + device.SendKeyEvent(keyevent.KEYCODE_DPAD_RIGHT) + device.SendKeyEvent(keyevent.KEYCODE_ENTER) + return m.group(2) + + return None + + class LocalDeviceInstrumentationTestRun( local_device_test_run.LocalDeviceTestRun): def __init__(self, env, test_instance):
diff --git a/build/android/pylib/monkey/test_runner.py b/build/android/pylib/monkey/test_runner.py index 19dd339..3fd1797 100644 --- a/build/android/pylib/monkey/test_runner.py +++ b/build/android/pylib/monkey/test_runner.py
@@ -10,8 +10,11 @@ from pylib import constants from pylib.base import base_test_result from pylib.base import base_test_runner +from pylib.device import device_errors from pylib.device import intent +_CHROME_PACKAGE = constants.PACKAGE_INFO['chrome'].package + class TestRunner(base_test_runner.BaseTestRunner): """A TestRunner instance runs a monkey test on a single device.""" @@ -87,9 +90,17 @@ test_name, base_test_result.ResultType.FAIL, log=output) if 'chrome' in self._options.package: logging.warning('Starting MinidumpUploadService...') + # TODO(jbudorick): Update this after upstreaming. + minidump_intent = intent.Intent( + action='%s.crash.ACTION_FIND_ALL' % _CHROME_PACKAGE, + package=self._package, + activity='%s.crash.MinidumpUploadService' % _CHROME_PACKAGE) try: - self.device.old_interface.StartCrashUploadService(self._package) - except AssertionError as e: - logging.error('Failed to start MinidumpUploadService: %s', e) + self.device.RunShellCommand( + ['am', 'startservice'] + minidump_intent.am_args, + as_root=True, check_return=True) + except device_errors.CommandFailedError: + logging.exception('Failed to start MinidumpUploadService') + results.AddResult(result) return results, False
diff --git a/build/android/pylib/remote/device/appurify_constants.py b/build/android/pylib/remote/device/appurify_constants.py new file mode 100644 index 0000000..93431782 --- /dev/null +++ b/build/android/pylib/remote/device/appurify_constants.py
@@ -0,0 +1,57 @@ +# 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. + +"""Defines a set of constants specific to appurify.""" + +# Appurify network config constants. +class NETWORK(object): + WIFI_1_BAR = 1 + SPRINT_4G_LTE_4_BARS = 2 + SPRINT_3G_5_BARS = 3 + SPRINT_3G_4_BARS = 4 + SPRINT_3G_3_BARS = 5 + SPRINT_3G_2_BARS = 6 + SPRINT_3G_1_BAR = 7 + SPRING_4G_1_BAR = 8 + VERIZON_3G_5_BARS = 9 + VERIZON_3G_4_BARS = 10 + VERIZON_3G_3_BARS = 11 + VERIZON_3G_2_BARS = 12 + VERIZON_3G_1_BAR = 13 + VERIZON_4G_1_BAR = 14 + ATANDT_3G_5_BARS = 15 + ATANDT_3G_4_BARS = 16 + ATANDT_3G_3_BARS = 17 + ATANDT_3G_2_BARS = 18 + ATANDT_3G_1_BAR = 19 + GENERIC_2G_4_BARS = 20 + GENERIC_2G_3_BARS = 21 + GENERIC_EVOLVED_EDGE = 22 + GENERIC_GPRS = 23 + GENERIC_ENHANCED_GPRS = 24 + GENERIC_LTE = 25 + GENERIC_HIGH_LATENCY_DNS = 26 + GENERIC_100_PERCENT_PACKET_LOSS = 27 + ATANDT_HSPA_PLUS = 28 + ATANDT_4G_LTE_4_BARS = 29 + VERIZON_4G_LTE_4_BARS = 30 + GENERIC_DIGITAL_SUBSCRIBE_LINE = 31 + WIFI_STARBUCKS_3_BARS = 32 + WIFI_STARBUCKS_4_BARS = 33 + WIFI_STARBUCKS_HIGH_TRAFFIC = 34 + WIFI_TARGET_1_BAR = 35 + WIFI_TARGET_3_BARS = 36 + WIFI_TARGET_4_BARS = 37 + PUBLIC_WIFI_MCDONALDS_5_BARS = 38 + PUBLIC_WIFI_MCDONALDS_4_BARS = 39 + PUBLIC_WIFI_MCDONALDS_2_BARS = 40 + PUBLIC_WIFI_MCDONALDS_1_BAR = 41 + PUBLIC_WIFI_KOHLS_5_BARS = 42 + PUBLIC_WIFI_KOHLS_4_BARS = 43 + PUBLIC_WIFI_KOHLS_2_BARS = 44 + PUBLIC_WIFI_ATANDT_5_BARS = 45 + PUBLIC_WIFI_ATANDT_4_BARS = 46 + PUBLIC_WIFI_ATANDT_2_BARS = 47 + PUBLIC_WIFI_ATANDT_1_BAR = 48 + BOINGO = 49 \ No newline at end of file
diff --git a/build/android/pylib/remote/device/remote_device_environment.py b/build/android/pylib/remote/device/remote_device_environment.py index 8875096c..b69c7b2e 100644 --- a/build/android/pylib/remote/device/remote_device_environment.py +++ b/build/android/pylib/remote/device/remote_device_environment.py
@@ -73,6 +73,7 @@ self._api_secret = device_json.get('api_secret', None) self._device_oem = device_json.get('device_oem', None) self._device_type = device_json.get('device_type', 'Android') + self._network_config = device_json.get('network_config', None) self._remote_device = device_json.get('remote_device', None) self._remote_device_minimum_os = device_json.get( 'remote_device_minimum_os', None) @@ -81,9 +82,7 @@ self._results_path = device_json.get('results_path', None) self._runner_package = device_json.get('runner_package', None) self._runner_type = device_json.get('runner_type', None) - if 'timeouts' in device_json: - for key in device_json['timeouts']: - self._timeouts[key] = device_json['timeouts'][key] + self._timeouts.update(device_json.get('timeouts', {})) def command_line_override( file_value, cmd_line_value, desc, print_value=True): @@ -107,6 +106,8 @@ self._device_oem, args.device_oem, 'device_oem') self._device_type = command_line_override( self._device_type, args.device_type, 'device_type') + self._network_config = command_line_override( + self._network_config, args.network_config, 'network_config') self._remote_device = command_line_override( self._remote_device, args.remote_device, 'remote_device') self._remote_device_minimum_os = command_line_override( @@ -325,6 +326,10 @@ return self._device['device_type_id'] @property + def network_config(self): + return self._network_config + + @property def only_output_failures(self): # TODO(jbudorick): Remove this once b/18981674 is fixed. return True
diff --git a/build/android/pylib/remote/device/remote_device_test_run.py b/build/android/pylib/remote/device/remote_device_test_run.py index 4a155acc1..7aa91ae 100644 --- a/build/android/pylib/remote/device/remote_device_test_run.py +++ b/build/android/pylib/remote/device/remote_device_test_run.py
@@ -14,6 +14,7 @@ from pylib import constants from pylib.base import test_run +from pylib.remote.device import appurify_constants from pylib.remote.device import appurify_sanitized from pylib.remote.device import remote_device_helper from pylib.utils import zip_utils @@ -230,7 +231,10 @@ self._test_id = self._UploadTestToDevice('robotium', test_path) logging.info('Setting config: %s' % config) - self._SetTestConfig('robotium', config) + appurify_configs = {} + if self._env.network_config: + appurify_configs['network'] = self._env.network_config + self._SetTestConfig('robotium', config, **appurify_configs) def _UploadAppToDevice(self, app_path): """Upload app to device.""" @@ -259,21 +263,32 @@ 'Unable to upload %s.' % test_path) return upload_results.json()['response']['test_id'] - def _SetTestConfig(self, runner_type, body): + def _SetTestConfig(self, runner_type, runner_configs, + network=appurify_constants.NETWORK.WIFI_1_BAR, + pcap=0, profiler=0, videocapture=0): """Generates and uploads config file for test. Args: - extras: Extra arguments to set in the config file. + runner_configs: Configs specific to the runner you are using. + network: Config to specify the network environment the devices running + the tests will be in. + pcap: Option to set the recording the of network traffic from the device. + profiler: Option to set the recording of CPU, memory, and network + transfer usage in the tests. + videocapture: Option to set video capture during the tests. + """ logging.info('Generating config file for test.') with tempfile.TemporaryFile() as config: config_data = [ - '[appurify]', - 'pcap=0', - 'profiler=0', - 'videocapture=0', - '[%s]' % runner_type + '[appurify]', + 'network=%s' % network, + 'pcap=%s' % pcap, + 'profiler=%s' % profiler, + 'videocapture=%s' % videocapture, + '[%s]' % runner_type ] - config_data.extend('%s=%s' % (k, v) for k, v in body.iteritems()) + config_data.extend( + '%s=%s' % (k, v) for k, v in runner_configs.iteritems()) config.write(''.join('%s\n' % l for l in config_data)) config.flush() config.seek(0) @@ -282,4 +297,4 @@ config_response = appurify_sanitized.api.config_upload( self._env.token, config, self._test_id) remote_device_helper.TestHttpResponse( - config_response, 'Unable to upload test config.') + config_response, 'Unable to upload test config.') \ No newline at end of file
diff --git a/build/android/pylib/utils/emulator.py b/build/android/pylib/utils/emulator.py index 81b9c98..26b9109 100644 --- a/build/android/pylib/utils/emulator.py +++ b/build/android/pylib/utils/emulator.py
@@ -19,6 +19,7 @@ from pylib import cmd_helper from pylib import constants from pylib import pexpect +from pylib.device import device_errors from pylib.device import device_utils from pylib.utils import time_profile @@ -394,33 +395,30 @@ """ seconds_waited = 0 number_of_waits = 2 # Make sure we can wfd twice - # TODO(jbudorick) Un-handroll this in the implementation switch. - adb_cmd = "adb -s %s %s" % (self.device_serial, 'wait-for-device') + + device = device_utils.DeviceUtils(self.device_serial) while seconds_waited < self._LAUNCH_TIMEOUT: try: - run_command.RunCommand(adb_cmd, - timeout_time=self._WAITFORDEVICE_TIMEOUT, - retry_count=1) + device.adb.WaitForDevice( + timeout=self._WAITFORDEVICE_TIMEOUT, retries=1) number_of_waits -= 1 if not number_of_waits: break - except errors.WaitForResponseTimedOutError: + except device_errors.CommandTimeoutError: seconds_waited += self._WAITFORDEVICE_TIMEOUT - adb_cmd = "adb -s %s %s" % (self.device_serial, 'kill-server') - run_command.RunCommand(adb_cmd) + device.adb.KillServer() self.popen.poll() if self.popen.returncode != None: raise EmulatorLaunchException('EMULATOR DIED') + if seconds_waited >= self._LAUNCH_TIMEOUT: raise EmulatorLaunchException('TIMEOUT with wait-for-device') + logging.info('Seconds waited on wait-for-device: %d', seconds_waited) if wait_for_boot: # Now that we checked for obvious problems, wait for a boot complete. # Waiting for the package manager is sometimes problematic. - # TODO(jbudorick) Convert this once waiting for the package manager and - # the external storage is no longer problematic. - d = device_utils.DeviceUtils(self.device_serial) - d.old_interface.WaitForSystemBootCompleted(self._WAITFORBOOT_TIMEOUT) + device.WaitUntilFullyBooted(timeout=self._WAITFORBOOT_TIMEOUT) def Shutdown(self): """Shuts down the process started by launch."""
diff --git a/build/android/test_runner.py b/build/android/test_runner.py index 23d46b71..c54ed28d 100755 --- a/build/android/test_runner.py +++ b/build/android/test_runner.py
@@ -152,6 +152,9 @@ 'Overrides all other flags.')) group.add_argument('--remote-device-timeout', type=int, help='Times to retry finding remote device') + group.add_argument('--network-config', type=int, + help='Integer that specifies the network environment ' + 'that the tests will be run in.') device_os_group = group.add_mutually_exclusive_group() device_os_group.add_argument('--remote-device-minimum-os',
diff --git a/build/build_config.h b/build/build_config.h index 7137b4be..d8c3db6 100644 --- a/build/build_config.h +++ b/build/build_config.h
@@ -61,11 +61,8 @@ #error Please add support for your platform in build/build_config.h #endif -#if defined(USE_OPENSSL) && defined(USE_NSS_CERTS) -// TODO(davidben): This constraint compares somewhat orthogonal things and will -// be fixed when BoringSSL with NSS for certificates is added as a build -// configuration. See https://crbug.com/462040. -#error Cannot use both OpenSSL and NSS +#if defined(USE_OPENSSL_CERTS) && defined(USE_NSS_CERTS) +#error Cannot use both OpenSSL and NSS for certificates #endif // For access to standard BSD features, use OS_BSD instead of a
diff --git a/build/common.gypi b/build/common.gypi index c5a8e73..e43adfd 100644 --- a/build/common.gypi +++ b/build/common.gypi
@@ -70,7 +70,10 @@ # certificates, use_openssl_certs must be set. 'use_openssl%': 0, - # Typedef X509Certificate::OSCertHandle to OpenSSL's struct X509*. + # Use OpenSSL for representing certificates. When targeting Android, + # the platform certificate library is used for certificate + # verification. On other targets, this flag also enables OpenSSL for + # certificate verification, but this configuration is unsupported. 'use_openssl_certs%': 0, # Disable viewport meta tag by default. @@ -681,20 +684,12 @@ }], # NSS usage. - ['(OS=="linux" or OS=="freebsd" or OS=="openbsd" or OS=="solaris") and use_openssl==0', { + ['(OS=="linux" or OS=="freebsd" or OS=="openbsd" or OS=="solaris")', { 'use_nss_certs%': 1, }, { 'use_nss_certs%': 0, }], - # When OpenSSL is used for SSL and crypto on Unix-like systems, use - # OpenSSL's certificate definition. - ['(OS=="linux" or OS=="freebsd" or OS=="openbsd" or OS=="solaris") and use_openssl==1', { - 'use_openssl_certs%': 1, - }, { - 'use_openssl_certs%': 0, - }], - # libudev usage. This currently only affects the content layer. ['OS=="linux" and embedded==0', { 'use_udev%': 1, @@ -5213,6 +5208,15 @@ }, { # asan != 0 'STRIPFLAGS': '-S', }], + ['branding=="Chrome" and buildtype=="Official"', { + 'OTHER_CFLAGS': [ + # The Google Chrome Framework dSYM generated by + # dsymutil has grown larger than 4GB, which + # dsymutil can't handle. Reduce the amount of debug + # symbols. + '-gline-tables-only', # See http://crbug.com/479841 + ] + }], ], }, # xcode_settings }, # configuration "Release"
diff --git a/build/config/BUILD.gn b/build/config/BUILD.gn index 471994d..22cb45a 100644 --- a/build/config/BUILD.gn +++ b/build/config/BUILD.gn
@@ -122,10 +122,11 @@ } if (use_openssl) { defines += [ "USE_OPENSSL=1" ] - if (use_openssl_certs) { - defines += [ "USE_OPENSSL_CERTS=1" ] - } - } else if (use_nss_certs) { + } + if (use_openssl_certs) { + defines += [ "USE_OPENSSL_CERTS=1" ] + } + if (use_nss_certs) { defines += [ "USE_NSS_CERTS=1" ] } if (use_ozone) {
diff --git a/build/config/android/internal_rules.gni b/build/config/android/internal_rules.gni index 679213f..a093879c 100644 --- a/build/config/android/internal_rules.gni +++ b/build/config/android/internal_rules.gni
@@ -226,7 +226,8 @@ dep_name = get_label_info(d, "name") possible_deps_configs += [ "$dep_gen_dir/$dep_name.build_config" ] } - rebase_possible_deps_configs = rebase_path(possible_deps_configs) + rebase_possible_deps_configs = + rebase_path(possible_deps_configs, root_build_dir) outputs = [ depfile, @@ -288,6 +289,19 @@ args += [ "--bypass-platform-checks" ] } + if (defined(invoker.apk_under_test)) { + deps += [ invoker.apk_under_test ] + apk_under_test_gen_dir = + get_label_info(invoker.apk_under_test, "target_gen_dir") + apk_under_test_name = get_label_info(invoker.apk_under_test, "name") + apk_under_test_config = + "$apk_under_test_gen_dir/$apk_under_test_name.build_config" + args += [ + "--tested-apk-config", + rebase_path(apk_under_test_config, root_build_dir), + ] + } + if (is_android_resources || is_apk) { assert(defined(invoker.resources_zip)) args += [
diff --git a/build/config/android/rules.gni b/build/config/android/rules.gni index 5c3bc24..7a49a5e 100644 --- a/build/config/android/rules.gni +++ b/build/config/android/rules.gni
@@ -1161,6 +1161,8 @@ # native_libs: List paths of native libraries to include in this apk. If these # libraries depend on other shared_library targets, those dependencies will # also be included in the apk. +# apk_under_test: For an instrumentation test apk, this is the target of the +# tested apk. # testonly: Marks this target as "test-only". # # DEPRECATED_java_in_dir: Directory containing java files. All .java files in @@ -1194,7 +1196,7 @@ assert(defined(invoker.final_apk_path) || defined(invoker.apk_name)) gen_dir = "$target_gen_dir/$target_name" base_path = "$gen_dir/$target_name" - _build_config = "$base_path.build_config" + _build_config = "$target_gen_dir/$target_name.build_config" resources_zip_path = "$base_path.resources.zip" all_resources_zip_path = "$base_path.resources.all.zip" jar_path = "$base_path.jar" @@ -1289,6 +1291,10 @@ deps = invoker.deps } + if (defined(invoker.apk_under_test)) { + apk_under_test = invoker.apk_under_test + } + native_libs = _native_libs }
diff --git a/build/config/crypto.gni b/build/config/crypto.gni index ee23569..7f090b7 100644 --- a/build/config/crypto.gni +++ b/build/config/crypto.gni
@@ -14,10 +14,13 @@ use_openssl = is_android || is_mac || is_nacl || is_win } -# True when we're using OpenSSL for certificate verification and storage. We -# only do this when we're using OpenSSL on desktop Linux systems. For other -# systems (Mac/Win/Android) we use the system certificate features. -use_openssl_certs = use_openssl && (is_linux || is_android) +# True when we're using OpenSSL for representing certificates. When targeting +# Android, the platform certificate library is used for certificate +# verification. On other targets, this flag also enables OpenSSL for certificate +# verification, but this configuration is unsupported. +use_openssl_certs = is_android -# Same meaning as use_openssl_certs but for NSS. -use_nss_certs = !use_openssl && is_linux +# True if NSS is used for certificate verification. Note that this is +# independent from use_openssl. It is possible to use OpenSSL for the crypto +# library, but NSS for the platform certificate library. +use_nss_certs = is_linux
diff --git a/build/config/features.gni b/build/config/features.gni index 7ed7034..dd7b081 100644 --- a/build/config/features.gni +++ b/build/config/features.gni
@@ -80,6 +80,10 @@ # Enable hole punching for the protected video. enable_video_hole = is_android + + # Enables browser side Content Decryption Modules. Required for embedders + # (e.g. Android and ChromeCast) that use a browser side CDM. + enable_browser_cdms = is_android } # Additional dependent variables ----------------------------------------------- @@ -102,8 +106,6 @@ enable_pepper_cdms = enable_plugins && (is_linux || is_mac || is_win) -enable_browser_cdms = is_android - # Enable basic printing support and UI. enable_basic_printing = !is_chromeos
diff --git a/build/gn_migration.gypi b/build/gn_migration.gypi index a34b9ca..76d3b59a 100644 --- a/build/gn_migration.gypi +++ b/build/gn_migration.gypi
@@ -52,7 +52,6 @@ '../chrome/chrome.gyp:interactive_ui_tests', '../chrome/chrome.gyp:load_library_perf_tests', '../chrome/chrome.gyp:performance_browser_tests', - '../chrome/chrome.gyp:service_discovery_sniffer', '../chrome/chrome.gyp:sync_integration_tests', '../chrome/chrome.gyp:sync_performance_tests', '../chrome/chrome.gyp:unit_tests', @@ -199,7 +198,6 @@ '../ui/display/display.gyp:display_unittests', '../ui/events/events.gyp:events_unittests', '../ui/gfx/gfx_tests.gyp:gfx_unittests', - '../ui/keyboard/keyboard.gyp:keyboard_unittests', '../ui/message_center/message_center.gyp:message_center_unittests', '../ui/snapshot/snapshot.gyp:snapshot_unittests', '../ui/touch_selection/ui_touch_selection.gyp:ui_touch_selection_unittests', @@ -225,6 +223,11 @@ '../extensions/shell/app_shell.gyp:app_shell_unittests', ], }], + ['enable_mdns==1', { + 'dependencies': [ + '../chrome/chrome.gyp:service_discovery_sniffer', + ] + }], ['remoting==1', { 'dependencies': [ '../remoting/remoting_all.gyp:remoting_all', @@ -259,6 +262,7 @@ '../ui/aura/aura.gyp:aura_bench', '../ui/aura/aura.gyp:aura_demo', '../ui/aura/aura.gyp:aura_unittests', + '../ui/keyboard/keyboard.gyp:keyboard_unittests', '../ui/wm/wm.gyp:wm_unittests', ], }], @@ -365,6 +369,16 @@ '../rlz/rlz.gyp:rlz_unittests', ], }], + ['OS=="android" or OS=="linux" or os_bsd==1', { + 'dependencies': [ + '../breakpad/breakpad.gyp:core-2-minidump', + '../breakpad/breakpad.gyp:microdump_stackwalk', + '../breakpad/breakpad.gyp:minidump_dump', + '../breakpad/breakpad.gyp:minidump_stackwalk', + '../breakpad/breakpad.gyp:symupload', + '../third_party/codesighs/codesighs.gyp:nm2tsv', + ], + }], ['OS=="linux"', { 'dependencies': [ '../breakpad/breakpad.gyp:breakpad_unittests', @@ -463,15 +477,6 @@ '../third_party/pdfium/samples/samples.gyp:pdfium_diff', '../win8/win8.gyp:metro_viewer', ], - }, { - 'dependencies': [ - '../breakpad/breakpad.gyp:core-2-minidump', - '../breakpad/breakpad.gyp:microdump_stackwalk', - '../breakpad/breakpad.gyp:minidump_dump', - '../breakpad/breakpad.gyp:minidump_stackwalk', - '../breakpad/breakpad.gyp:symupload', - '../third_party/codesighs/codesighs.gyp:nm2tsv', - ], }], ], }, @@ -669,7 +674,6 @@ 'type': 'none', 'dependencies': [ 'All', - 'aura_builder', 'blink_tests', 'chromium_builder_asan', 'chromium_builder_chromedriver', @@ -680,6 +684,11 @@ 'chromium_gpu_debug_builder', ], 'conditions': [ + ['use_aura==1', { + 'dependencies': [ + 'aura_builder', + ] + }], ['OS=="win"', { 'dependencies': [ 'chromium_builder',
diff --git a/build/linux/system.gyp b/build/linux/system.gyp index 5333798..cc6e81b 100644 --- a/build/linux/system.gyp +++ b/build/linux/system.gyp
@@ -1180,8 +1180,7 @@ 'dependencies': [ '../../third_party/boringssl/boringssl.gyp:boringssl', ], - }], - ['use_openssl==0', { + }, { 'dependencies': [ '../../net/third_party/nss/ssl.gyp:libssl', ], @@ -1191,6 +1190,13 @@ # before other includes, as we are shadowing system headers. '<(DEPTH)/net/third_party/nss/ssl', ], + }, + }], + # Link in the system NSS if it is used for either the internal + # crypto library (use_openssl==0) or platform certificate + # library (use_nss_certs==1). + ['use_openssl==0 or use_nss_certs==1', { + 'direct_dependent_settings': { 'cflags': [ '<!@(<(pkg-config) --cflags nss)', ], @@ -1203,15 +1209,17 @@ '<!@(<(pkg-config) --libs-only-l nss | sed -e "s/-lssl3//")', ], }, - }], - ['use_openssl==0 and clang==1', { - 'direct_dependent_settings': { - 'cflags': [ - # There is a broken header guard in /usr/include/nss/secmod.h: - # https://bugzilla.mozilla.org/show_bug.cgi?id=884072 - '-Wno-header-guard', - ], - }, + 'conditions': [ + ['clang==1', { + 'direct_dependent_settings': { + 'cflags': [ + # There is a broken header guard in /usr/include/nss/secmod.h: + # https://bugzilla.mozilla.org/show_bug.cgi?id=884072 + '-Wno-header-guard', + ], + }, + }], + ], }], ] }],
diff --git a/build/linux/unbundle/libvpx.gyp b/build/linux/unbundle/libvpx.gyp index cdcf6fa..75671c5 100644 --- a/build/linux/unbundle/libvpx.gyp +++ b/build/linux/unbundle/libvpx.gyp
@@ -14,16 +14,17 @@ 'variables': { 'headers_root_path': 'source/libvpx', 'header_filenames': [ - 'vpx/vpx_codec_impl_bottom.h', - 'vpx/vpx_image.h', - 'vpx/vpx_decoder.h', 'vpx/vp8.h', - 'vpx/vpx_codec.h', - 'vpx/vpx_codec_impl_top.h', 'vpx/vp8cx.h', - 'vpx/vpx_integer.h', 'vpx/vp8dx.h', + 'vpx/vpx_codec.h', + 'vpx/vpx_codec_impl_bottom.h', + 'vpx/vpx_codec_impl_top.h', + 'vpx/vpx_decoder.h', 'vpx/vpx_encoder.h', + 'vpx/vpx_frame_buffer.h', + 'vpx/vpx_image.h', + 'vpx/vpx_integer.h', ], }, 'includes': [
diff --git a/build/sanitizers/tsan_suppressions.cc b/build/sanitizers/tsan_suppressions.cc index f7aa1667..352b41f 100644 --- a/build/sanitizers/tsan_suppressions.cc +++ b/build/sanitizers/tsan_suppressions.cc
@@ -122,6 +122,9 @@ "race:base::PowerMonitor::RemoveObserver\n" "race:base::PowerMonitor::IsOnBatteryPower\n" +// http://crbug.com/258935 +"race:base::Thread::StopSoon\n" + // http://crbug.com/268941 "race:tracked_objects::ThreadData::tls_index_\n" @@ -292,6 +295,9 @@ // https://crbug.com/430533 "race:TileTaskGraphRunner::Run\n" +// https://crbug.com/437044 +"race:SkEventTracer\n" + // https://crbug.com/448203 "race:blink::RemoteFrame::detach\n"
diff --git a/build/util/lastchange.py b/build/util/lastchange.py index d1c33e8..1a7f519 100755 --- a/build/util/lastchange.py +++ b/build/util/lastchange.py
@@ -115,6 +115,7 @@ for line in reversed(output.splitlines()): if line.startswith('Cr-Commit-Position:'): pos = line.rsplit()[-1].strip() + break if not pos: return VersionInfo('git', hsh) return VersionInfo('git', '%s-%s' % (hsh, pos))
diff --git a/cc/OWNERS b/cc/OWNERS index 5692349..b0bce1d 100644 --- a/cc/OWNERS +++ b/cc/OWNERS
@@ -28,6 +28,7 @@ brianderson@chromium.org skyostil@chromium.org mithro@mithis.com +sunnyps@chromium.org # texture uploading brianderson@chromium.org
diff --git a/cc/blink/web_layer_impl.cc b/cc/blink/web_layer_impl.cc index 3c688a6..8bad92a0 100644 --- a/cc/blink/web_layer_impl.cc +++ b/cc/blink/web_layer_impl.cc
@@ -365,12 +365,27 @@ const WebVector<std::pair<int64_t, WebRect>>& requests) { std::vector<cc::FrameTimingRequest> frame_timing_requests(requests.size()); for (size_t i = 0; i < requests.size(); ++i) { - frame_timing_requests.push_back(cc::FrameTimingRequest( - requests[i].first, gfx::Rect(requests[i].second))); + frame_timing_requests[i] = cc::FrameTimingRequest( + requests[i].first, gfx::Rect(requests[i].second)); } layer_->SetFrameTimingRequests(frame_timing_requests); } +WebVector<std::pair<int64_t, WebRect>> WebLayerImpl::frameTimingRequests() + const { + const std::vector<cc::FrameTimingRequest>& frame_timing_requests = + layer_->FrameTimingRequests(); + + size_t num_requests = frame_timing_requests.size(); + + WebVector<std::pair<int64_t, WebRect>> result(num_requests); + for (size_t i = 0; i < num_requests; ++i) { + result[i] = std::make_pair(frame_timing_requests[i].id(), + frame_timing_requests[i].rect()); + } + return result; +} + void WebLayerImpl::setTouchEventHandlerRegion(const WebVector<WebRect>& rects) { cc::Region region; for (size_t i = 0; i < rects.size(); ++i)
diff --git a/cc/blink/web_layer_impl.h b/cc/blink/web_layer_impl.h index 0560bed..beaf3b51 100644 --- a/cc/blink/web_layer_impl.h +++ b/cc/blink/web_layer_impl.h
@@ -128,6 +128,8 @@ virtual blink::WebScrollBlocksOn scrollBlocksOn() const; virtual void setFrameTimingRequests( const blink::WebVector<std::pair<int64_t, blink::WebRect>>& requests); + virtual blink::WebVector<std::pair<int64_t, blink::WebRect>> + frameTimingRequests() const; virtual void setIsContainerForFixedPositionLayers(bool is_container); virtual bool isContainerForFixedPositionLayers() const; virtual void setPositionConstraint(
diff --git a/cc/layers/layer.cc b/cc/layers/layer.cc index d16c673..56b8c739 100644 --- a/cc/layers/layer.cc +++ b/cc/layers/layer.cc
@@ -360,6 +360,7 @@ clip_tree_index())) { if (clip_node->owner_id == id()) { clip_node->data.clip.set_size(size); + layer_tree_host_->property_trees()->clip_tree.set_needs_update(true); } } @@ -614,6 +615,7 @@ transform_node->data.update_post_local_transform(position, transform_origin()); transform_node->data.needs_local_transform_update = true; + layer_tree_host_->property_trees()->transform_tree.set_needs_update(true); SetNeedsCommitNoRebuild(); return; } @@ -663,6 +665,8 @@ Are2dAxisAligned(transform_, transform, &invertible); transform_node->data.local = transform; transform_node->data.needs_local_transform_update = true; + layer_tree_host_->property_trees()->transform_tree.set_needs_update( + true); if (preserves_2d_axis_alignment) SetNeedsCommitNoRebuild(); else @@ -693,9 +697,11 @@ layer_tree_host_->property_trees()->transform_tree.Node( transform_tree_index())) { if (transform_node->owner_id == id()) { + transform_node->data.update_pre_local_transform(transform_origin); transform_node->data.update_post_local_transform(position(), transform_origin); transform_node->data.needs_local_transform_update = true; + layer_tree_host_->property_trees()->transform_tree.set_needs_update(true); SetNeedsCommitNoRebuild(); return; } @@ -789,6 +795,7 @@ transform_node->data.scroll_offset = gfx::ScrollOffsetToVector2dF(CurrentScrollOffset()); transform_node->data.needs_local_transform_update = true; + layer_tree_host_->property_trees()->transform_tree.set_needs_update(true); SetNeedsCommitNoRebuild(); return; } @@ -828,6 +835,7 @@ transform_node->data.scroll_offset = gfx::ScrollOffsetToVector2dF(CurrentScrollOffset()); transform_node->data.needs_local_transform_update = true; + layer_tree_host_->property_trees()->transform_tree.set_needs_update(true); needs_rebuild = false; } } @@ -1319,6 +1327,8 @@ node->data.local = transform; node->data.needs_local_transform_update = true; node->data.is_animated = true; + layer_tree_host_->property_trees()->transform_tree.set_needs_update( + true); } } }
diff --git a/cc/layers/layer.h b/cc/layers/layer.h index dd554389..6f10725b 100644 --- a/cc/layers/layer.h +++ b/cc/layers/layer.h
@@ -546,6 +546,11 @@ // Sets new frame timing requests for this layer. void SetFrameTimingRequests(const std::vector<FrameTimingRequest>& requests); + // Accessor for unit tests + const std::vector<FrameTimingRequest>& FrameTimingRequests() const { + return frame_timing_requests_; + } + void DidBeginTracing(); protected:
diff --git a/cc/layers/picture_layer_impl.cc b/cc/layers/picture_layer_impl.cc index e9188ec6..3fb0256 100644 --- a/cc/layers/picture_layer_impl.cc +++ b/cc/layers/picture_layer_impl.cc
@@ -706,6 +706,7 @@ int viewport_width = gpu_raster_max_texture_size_.width(); int viewport_height = gpu_raster_max_texture_size_.height(); default_tile_width = viewport_width; + // Also, increase the height proportionally as the width decreases, and // pad by our border texels to make the tiles exactly match the viewport. int divisor = 4; @@ -714,7 +715,11 @@ if (content_bounds.width() <= viewport_width / 4) divisor = 1; default_tile_height = RoundUp(viewport_height, divisor) / divisor; + + // Grow default sizes to account for overlapping border texels. + default_tile_width += 2 * PictureLayerTiling::kBorderTexels; default_tile_height += 2 * PictureLayerTiling::kBorderTexels; + default_tile_height = std::max(default_tile_height, kMinHeightForGpuRasteredTile); } else {
diff --git a/cc/layers/picture_layer_impl_unittest.cc b/cc/layers/picture_layer_impl_unittest.cc index 966186f..2d2e330 100644 --- a/cc/layers/picture_layer_impl_unittest.cc +++ b/cc/layers/picture_layer_impl_unittest.cc
@@ -299,6 +299,16 @@ EXPECT_EQ(0u, active_layer_->tilings()->num_tilings()); } + size_t NumberOfTilesRequired(PictureLayerTiling* tiling) { + size_t num_required = 0; + std::vector<Tile*> tiles = tiling->AllTilesForTesting(); + for (size_t i = 0; i < tiles.size(); ++i) { + if (tiles[i]->required_for_activation()) + num_required++; + } + return num_required; + } + void AssertAllTilesRequired(PictureLayerTiling* tiling) { std::vector<Tile*> tiles = tiling->AllTilesForTesting(); for (size_t i = 0; i < tiles.size(); ++i) @@ -2510,6 +2520,30 @@ EXPECT_EQ(1u, pending_layer_->tilings()->num_tilings()); } +TEST_F(PictureLayerImplTest, RequiredTilesWithGpuRasterization) { + host_impl_.SetUseGpuRasterization(true); + + gfx::Size viewport_size(1000, 1000); + host_impl_.SetViewportSize(viewport_size); + + gfx::Size layer_bounds(4000, 4000); + SetupDefaultTrees(layer_bounds); + EXPECT_TRUE(host_impl_.use_gpu_rasterization()); + + // Should only have the high-res tiling. + EXPECT_EQ(1u, pending_layer_->tilings()->num_tilings()); + + active_layer_->SetAllTilesReady(); + pending_layer_->HighResTiling()->UpdateAllTilePrioritiesForTesting(); + + // High res tiling should have 64 tiles (4x16 tile grid). + EXPECT_EQ(64u, pending_layer_->HighResTiling()->AllTilesForTesting().size()); + + // Visible viewport should be covered by 4 tiles. No other + // tiles should be required for activation. + EXPECT_EQ(4u, NumberOfTilesRequired(pending_layer_->HighResTiling())); +} + TEST_F(PictureLayerImplTest, NoTilingIfDoesNotDrawContent) { // Set up layers with tilings. SetupDefaultTrees(gfx::Size(10, 10)); @@ -4877,7 +4911,7 @@ layer->set_gpu_raster_max_texture_size(host_impl_.device_viewport_size()); result = layer->CalculateTileSize(gfx::Size(10000, 10000)); - EXPECT_EQ(result.width(), 2000); + EXPECT_EQ(result.width(), 2000 + 2 * PictureLayerTiling::kBorderTexels); EXPECT_EQ(result.height(), 500 + 2); // Clamp and round-up, when smaller than viewport.
diff --git a/cc/quads/list_container.cc b/cc/quads/list_container.cc index c5cbf87fa..d3442d4 100644 --- a/cc/quads/list_container.cc +++ b/cc/quads/list_container.cc
@@ -138,6 +138,26 @@ return storage_[id]; } + size_t FirstInnerListId() const { + // |size_| > 0 means that at least one vector in |storage_| will be + // non-empty. + DCHECK_GT(size_, 0u); + size_t id = 0; + while (storage_[id]->size == 0) + ++id; + return id; + } + + size_t LastInnerListId() const { + // |size_| > 0 means that at least one vector in |storage_| will be + // non-empty. + DCHECK_GT(size_, 0u); + size_t id = list_count_ - 1; + while (storage_[id]->size == 0) + --id; + return id; + } + void AllocateNewList(size_t list_size) { ++list_count_; scoped_ptr<InnerList> new_list(new InnerList); @@ -210,12 +230,16 @@ typename ListContainerCharAllocator::InnerList* list = ptr_to_container->InnerListById(vector_index); if (item_iterator == list->LastElement()) { - if (vector_index < ptr_to_container->list_count() - 1) { + ++vector_index; + while (vector_index < ptr_to_container->list_count()) { + if (ptr_to_container->InnerListById(vector_index)->size != 0) + break; ++vector_index; - item_iterator = ptr_to_container->InnerListById(vector_index)->Begin(); - } else { - item_iterator = NULL; } + if (vector_index < ptr_to_container->list_count()) + item_iterator = ptr_to_container->InnerListById(vector_index)->Begin(); + else + item_iterator = NULL; } else { item_iterator += list->step; } @@ -229,8 +253,16 @@ typename ListContainerCharAllocator::InnerList* list = ptr_to_container->InnerListById(vector_index); if (item_iterator == list->Begin()) { - if (vector_index > 0) { + --vector_index; + // Since |vector_index| is unsigned, we compare < list_count() instead of + // comparing >= 0, as the variable will wrap around when it goes out of + // range (below 0). + while (vector_index < ptr_to_container->list_count()) { + if (ptr_to_container->InnerListById(vector_index)->size != 0) + break; --vector_index; + } + if (vector_index < ptr_to_container->list_count()) { item_iterator = ptr_to_container->InnerListById(vector_index)->LastElement(); } else { @@ -281,17 +313,18 @@ typename ListContainer<BaseElementType>::ConstReverseIterator ListContainer<BaseElementType>::crbegin() const { if (data_->IsEmpty()) - return ConstReverseIterator(data_.get(), 0, NULL, 0); + return crend(); - size_t last_id = data_->list_count() - 1; - return ConstReverseIterator( - data_.get(), last_id, data_->InnerListById(last_id)->LastElement(), 0); + size_t id = data_->LastInnerListId(); + return ConstReverseIterator(data_.get(), id, + data_->InnerListById(id)->LastElement(), 0); } template <typename BaseElementType> typename ListContainer<BaseElementType>::ConstReverseIterator ListContainer<BaseElementType>::crend() const { - return ConstReverseIterator(data_.get(), 0, NULL, size()); + return ConstReverseIterator(data_.get(), static_cast<size_t>(-1), NULL, + size()); } template <typename BaseElementType> @@ -310,26 +343,27 @@ typename ListContainer<BaseElementType>::ReverseIterator ListContainer<BaseElementType>::rbegin() { if (data_->IsEmpty()) - return ReverseIterator(data_.get(), 0, NULL, 0); + return rend(); - size_t last_id = data_->list_count() - 1; - return ReverseIterator( - data_.get(), last_id, data_->InnerListById(last_id)->LastElement(), 0); + size_t id = data_->LastInnerListId(); + return ReverseIterator(data_.get(), id, + data_->InnerListById(id)->LastElement(), 0); } template <typename BaseElementType> typename ListContainer<BaseElementType>::ReverseIterator ListContainer<BaseElementType>::rend() { - return ReverseIterator(data_.get(), 0, NULL, size()); + return ReverseIterator(data_.get(), static_cast<size_t>(-1), NULL, size()); } template <typename BaseElementType> typename ListContainer<BaseElementType>::ConstIterator ListContainer<BaseElementType>::cbegin() const { if (data_->IsEmpty()) - return ConstIterator(data_.get(), 0, NULL, 0); + return cend(); - return ConstIterator(data_.get(), 0, data_->InnerListById(0)->Begin(), 0); + size_t id = data_->FirstInnerListId(); + return ConstIterator(data_.get(), id, data_->InnerListById(id)->Begin(), 0); } template <typename BaseElementType> @@ -338,8 +372,8 @@ if (data_->IsEmpty()) return ConstIterator(data_.get(), 0, NULL, size()); - size_t last_id = data_->list_count() - 1; - return ConstIterator(data_.get(), last_id, NULL, size()); + size_t id = data_->list_count(); + return ConstIterator(data_.get(), id, NULL, size()); } template <typename BaseElementType> @@ -358,9 +392,10 @@ typename ListContainer<BaseElementType>::Iterator ListContainer<BaseElementType>::begin() { if (data_->IsEmpty()) - return Iterator(data_.get(), 0, NULL, 0); + return end(); - return Iterator(data_.get(), 0, data_->InnerListById(0)->Begin(), 0); + size_t id = data_->FirstInnerListId(); + return Iterator(data_.get(), id, data_->InnerListById(id)->Begin(), 0); } template <typename BaseElementType> @@ -369,8 +404,8 @@ if (data_->IsEmpty()) return Iterator(data_.get(), 0, NULL, size()); - size_t last_id = data_->list_count() - 1; - return Iterator(data_.get(), last_id, NULL, size()); + size_t id = data_->list_count(); + return Iterator(data_.get(), id, NULL, size()); } template <typename BaseElementType>
diff --git a/cc/quads/list_container_unittest.cc b/cc/quads/list_container_unittest.cc index 68e6061..5de1ddd 100644 --- a/cc/quads/list_container_unittest.cc +++ b/cc/quads/list_container_unittest.cc
@@ -487,6 +487,87 @@ } } +TEST(ListContainerTest, DeletionAllInAllocation) { + const size_t kReserve = 10; + ListContainer<DrawQuad> list(kLargestQuadSize, kReserve); + std::vector<SimpleDrawQuad*> sdq_list; + // Add enough quads to cause another allocation. + for (size_t i = 0; i < kReserve + 1; ++i) { + sdq_list.push_back(list.AllocateAndConstruct<SimpleDrawQuad>()); + sdq_list.back()->set_value(static_cast<int>(i)); + } + EXPECT_EQ(kReserve + 1, list.size()); + + // Remove everything in the first allocation. + for (size_t i = 0; i < kReserve; ++i) + list.EraseAndInvalidateAllPointers(list.begin()); + EXPECT_EQ(1u, list.size()); + + // The last quad is left. + SimpleDrawQuad* quad = static_cast<SimpleDrawQuad*>(*list.begin()); + EXPECT_EQ(static_cast<int>(kReserve), quad->get_value()); + + // Remove the quad from the 2nd allocation. + list.EraseAndInvalidateAllPointers(list.begin()); + EXPECT_EQ(0u, list.size()); +} + +TEST(ListContainerTest, DeletionAllInAllocationReversed) { + const size_t kReserve = 10; + ListContainer<DrawQuad> list(kLargestQuadSize, kReserve); + std::vector<SimpleDrawQuad*> sdq_list; + // Add enough quads to cause another allocation. + for (size_t i = 0; i < kReserve + 1; ++i) { + sdq_list.push_back(list.AllocateAndConstruct<SimpleDrawQuad>()); + sdq_list.back()->set_value(static_cast<int>(i)); + } + EXPECT_EQ(kReserve + 1, list.size()); + + // Remove everything in the 2nd allocation. + auto it = list.begin(); + for (size_t i = 0; i < kReserve; ++i) + ++it; + list.EraseAndInvalidateAllPointers(it); + + // The 2nd-last quad is next, and the rest of the quads exist. + size_t i = kReserve - 1; + for (auto it = list.rbegin(); it != list.rend(); ++it) { + SimpleDrawQuad* quad = static_cast<SimpleDrawQuad*>(*it); + EXPECT_EQ(static_cast<int>(i), quad->get_value()); + --i; + } + + // Can forward iterate too. + i = 0; + for (auto it = list.begin(); it != list.end(); ++it) { + SimpleDrawQuad* quad = static_cast<SimpleDrawQuad*>(*it); + EXPECT_EQ(static_cast<int>(i), quad->get_value()); + ++i; + } + + // Remove the last thing from the 1st allocation. + it = list.begin(); + for (size_t i = 0; i < kReserve - 1; ++i) + ++it; + list.EraseAndInvalidateAllPointers(it); + + // The 2nd-last quad is next, and the rest of the quads exist. + i = kReserve - 2; + for (auto it = list.rbegin(); it != list.rend(); ++it) { + SimpleDrawQuad* quad = static_cast<SimpleDrawQuad*>(*it); + EXPECT_EQ(static_cast<int>(i), quad->get_value()); + --i; + } + + // Can forward iterate too. + i = 0; + for (auto it = list.begin(); it != list.end(); ++it) { + SimpleDrawQuad* quad = static_cast<SimpleDrawQuad*>(*it); + EXPECT_EQ(static_cast<int>(i), quad->get_value()); + ++i; + } +} + TEST(ListContainerTest, SimpleIterationAndManipulation) { ListContainer<DrawQuad> list(kLargestQuadSize); std::vector<SimpleDrawQuad*> sdq_list;
diff --git a/cc/trees/draw_property_utils.cc b/cc/trees/draw_property_utils.cc index 9295d1aa..83b91f4 100644 --- a/cc/trees/draw_property_utils.cc +++ b/cc/trees/draw_property_utils.cc
@@ -258,6 +258,8 @@ } // namespace void ComputeClips(ClipTree* clip_tree, const TransformTree& transform_tree) { + if (!clip_tree->needs_update()) + return; for (int i = 0; i < static_cast<int>(clip_tree->size()); ++i) { ClipNode* clip_node = clip_tree->Node(i); @@ -339,17 +341,23 @@ clip_node->data.combined_clip.Intersect(clip_node->data.clip); } + clip_tree->set_needs_update(false); } void ComputeTransforms(TransformTree* transform_tree) { + if (!transform_tree->needs_update()) + return; for (int i = 1; i < static_cast<int>(transform_tree->size()); ++i) transform_tree->UpdateTransforms(i); + transform_tree->set_needs_update(false); } template <typename LayerType> void ComputeVisibleRectsUsingPropertyTreesInternal( LayerType* root_layer, PropertyTrees* property_trees) { + if (property_trees->transform_tree.needs_update()) + property_trees->clip_tree.set_needs_update(true); ComputeTransforms(&property_trees->transform_tree); ComputeClips(&property_trees->clip_tree, property_trees->transform_tree);
diff --git a/cc/trees/latency_info_swap_promise_monitor.cc b/cc/trees/latency_info_swap_promise_monitor.cc index 83f5999..2b56f68c 100644 --- a/cc/trees/latency_info_swap_promise_monitor.cc +++ b/cc/trees/latency_info_swap_promise_monitor.cc
@@ -82,10 +82,9 @@ if (!new_sequence_number) return; ui::LatencyInfo new_latency; - new_latency.AddLatencyNumber( + new_latency.AddLatencyNumberWithTraceName( ui::INPUT_EVENT_LATENCY_BEGIN_SCROLL_UPDATE_MAIN_COMPONENT, 0, - new_sequence_number); - new_latency.TraceEventType("ScrollUpdate"); + new_sequence_number, "ScrollUpdate"); new_latency.CopyLatencyFrom( *latency_, ui::INPUT_EVENT_LATENCY_FORWARD_SCROLL_UPDATE_TO_MAIN_COMPONENT);
diff --git a/cc/trees/layer_tree_host_common.cc b/cc/trees/layer_tree_host_common.cc index 8683490..31f64d6 100644 --- a/cc/trees/layer_tree_host_common.cc +++ b/cc/trees/layer_tree_host_common.cc
@@ -2557,25 +2557,27 @@ PreCalculateMetaInformationRecursiveData recursive_data; PreCalculateMetaInformation(inputs->root_layer, &recursive_data); - if (!inputs->verify_property_trees) { - std::vector<AccumulatedSurfaceState<LayerType>> accumulated_surface_state; - CalculateDrawPropertiesInternal<LayerType>( - inputs->root_layer, globals, data_for_recursion, - inputs->render_surface_layer_list, &dummy_layer_list, - &accumulated_surface_state, - inputs->current_render_surface_layer_list_id); - } else { - { - TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug.cdp-perf"), - "LayerTreeHostCommon::CalculateDrawProperties"); - std::vector<AccumulatedSurfaceState<LayerType>> accumulated_surface_state; - CalculateDrawPropertiesInternal<LayerType>( - inputs->root_layer, globals, data_for_recursion, - inputs->render_surface_layer_list, &dummy_layer_list, - &accumulated_surface_state, - inputs->current_render_surface_layer_list_id); - } + const bool should_measure_property_tree_performance = + inputs->verify_property_trees && + (property_tree_option == BUILD_PROPERTY_TREES_IF_NEEDED); + if (should_measure_property_tree_performance) { + TRACE_EVENT_BEGIN0(TRACE_DISABLED_BY_DEFAULT("cc.debug.cdp-perf"), + "LayerTreeHostCommon::CalculateDrawProperties"); + } + + std::vector<AccumulatedSurfaceState<LayerType>> accumulated_surface_state; + CalculateDrawPropertiesInternal<LayerType>( + inputs->root_layer, globals, data_for_recursion, + inputs->render_surface_layer_list, &dummy_layer_list, + &accumulated_surface_state, inputs->current_render_surface_layer_list_id); + + if (should_measure_property_tree_performance) { + TRACE_EVENT_END0(TRACE_DISABLED_BY_DEFAULT("cc.debug.cdp-perf"), + "LayerTreeHostCommon::CalculateDrawProperties"); + } + + if (inputs->verify_property_trees) { // For testing purposes, sometimes property trees need to be built on the // compositor thread, so this can't just switch on Layer vs LayerImpl, // even though in practice only the main thread builds property trees. @@ -2584,14 +2586,24 @@ // The translation from layer to property trees is an intermediate // state. We will eventually get these data passed directly to the // compositor. - TRACE_EVENT0( - TRACE_DISABLED_BY_DEFAULT("cc.debug.cdp-perf"), - "LayerTreeHostCommon::ComputeVisibleRectsWithPropertyTrees"); + 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_application_layer, inputs->page_scale_factor, inputs->device_scale_factor, gfx::Rect(inputs->device_viewport_size), inputs->device_transform, inputs->property_trees); + + 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: {
diff --git a/cc/trees/layer_tree_host_common_unittest.cc b/cc/trees/layer_tree_host_common_unittest.cc index 3efac5b..e484b42 100644 --- a/cc/trees/layer_tree_host_common_unittest.cc +++ b/cc/trees/layer_tree_host_common_unittest.cc
@@ -9195,5 +9195,31 @@ EXPECT_TRUE(host->property_trees()->needs_rebuild); } +TEST_F(LayerTreeHostCommonTest, ChangeTransformOrigin) { + scoped_refptr<Layer> root = Layer::Create(); + scoped_refptr<LayerWithForcedDrawsContent> child = + make_scoped_refptr(new LayerWithForcedDrawsContent()); + root->AddChild(child); + + scoped_ptr<FakeLayerTreeHost> host(CreateFakeLayerTreeHost()); + host->SetRootLayer(root); + + gfx::Transform identity_matrix; + gfx::Transform scale_matrix; + scale_matrix.Scale(2.f, 2.f); + SetLayerPropertiesForTesting(root.get(), identity_matrix, gfx::Point3F(), + gfx::PointF(), gfx::Size(100, 100), true, false); + SetLayerPropertiesForTesting(child.get(), scale_matrix, gfx::Point3F(), + gfx::PointF(), gfx::Size(10, 10), true, false); + + ExecuteCalculateDrawProperties(root.get()); + EXPECT_EQ(gfx::Rect(10, 10), child->visible_rect_from_property_trees()); + + child->SetTransformOrigin(gfx::Point3F(10.f, 10.f, 10.f)); + + ExecuteCalculateDrawProperties(root.get()); + EXPECT_EQ(gfx::Rect(5, 5, 5, 5), child->visible_rect_from_property_trees()); +} + } // namespace } // namespace cc
diff --git a/cc/trees/property_tree.cc b/cc/trees/property_tree.cc index b26e884..7f384456 100644 --- a/cc/trees/property_tree.cc +++ b/cc/trees/property_tree.cc
@@ -12,7 +12,8 @@ namespace cc { template <typename T> -PropertyTree<T>::PropertyTree() { +PropertyTree<T>::PropertyTree() + : needs_update_(false) { nodes_.push_back(T()); back()->id = 0; back()->parent_id = -1; @@ -62,6 +63,13 @@ TransformNodeData::~TransformNodeData() { } +void TransformNodeData::update_pre_local_transform( + const gfx::Point3F& transform_origin) { + pre_local.MakeIdentity(); + pre_local.Translate3d(-transform_origin.x(), -transform_origin.y(), + -transform_origin.z()); +} + void TransformNodeData::update_post_local_transform( const gfx::PointF& position, const gfx::Point3F& transform_origin) {
diff --git a/cc/trees/property_tree.h b/cc/trees/property_tree.h index fc8f5a2..42e2e169 100644 --- a/cc/trees/property_tree.h +++ b/cc/trees/property_tree.h
@@ -105,6 +105,8 @@ is_invertible = to_parent.IsInvertible(); } + void update_pre_local_transform(const gfx::Point3F& transform_origin); + void update_post_local_transform(const gfx::PointF& position, const gfx::Point3F& transform_origin); }; @@ -146,9 +148,14 @@ void clear(); size_t size() const { return nodes_.size(); } + void set_needs_update(bool needs_update) { needs_update_ = needs_update; } + bool needs_update() const { return needs_update_; } + private: // Copy and assign are permitted. This is how we do tree sync. std::vector<T> nodes_; + + bool needs_update_; }; class CC_EXPORT TransformTree final : public PropertyTree<TransformNode> {
diff --git a/cc/trees/property_tree_builder.cc b/cc/trees/property_tree_builder.cc index 62f6313..269a71e 100644 --- a/cc/trees/property_tree_builder.cc +++ b/cc/trees/property_tree_builder.cc
@@ -287,9 +287,7 @@ } node->data.local = layer->transform(); - node->data.pre_local.Translate3d(-layer->transform_origin().x(), - -layer->transform_origin().y(), - -layer->transform_origin().z()); + node->data.update_pre_local_transform(layer->transform_origin()); node->data.needs_local_transform_update = true; data_from_ancestor.transform_tree->UpdateTransforms(node->id); @@ -414,6 +412,12 @@ data_for_recursion.clip_tree->Insert(root_clip, 0); BuildPropertyTreesInternal(root_layer, data_for_recursion); property_trees->needs_rebuild = false; + + // The transform tree is kept up-to-date as it is built, but the + // combined_clips stored in the clip tree aren't computed during tree + // building. + property_trees->transform_tree.set_needs_update(false); + property_trees->clip_tree.set_needs_update(true); } void PropertyTreeBuilder::BuildPropertyTrees(
diff --git a/cc/trees/property_tree_unittest.cc b/cc/trees/property_tree_unittest.cc index 37fd523..d8ee10e 100644 --- a/cc/trees/property_tree_unittest.cc +++ b/cc/trees/property_tree_unittest.cc
@@ -109,6 +109,7 @@ tree.Node(grand_child)->data.flattens_inherited_transform = true; tree.Node(grand_child)->data.local = rotation_about_x; + tree.set_needs_update(true); ComputeTransforms(&tree); gfx::Transform flattened_rotation_about_x = rotation_about_x; @@ -136,6 +137,7 @@ // Remove flattening at grand_child, and recompute transforms. tree.Node(grand_child)->data.flattens_inherited_transform = false; + tree.set_needs_update(true); ComputeTransforms(&tree); EXPECT_TRANSFORMATION_MATRIX_EQ(rotation_about_x * rotation_about_x, @@ -341,6 +343,7 @@ tree.Node(grand_child)->data.target_id = grand_child; tree.Node(grand_child)->data.flattens_inherited_transform = true; + tree.set_needs_update(true); ComputeTransforms(&tree); gfx::Transform flattened_rotation_about_x = rotation_about_x;
diff --git a/chrome/VERSION b/chrome/VERSION index abcd34d..721243e 100644 --- a/chrome/VERSION +++ b/chrome/VERSION
@@ -1,4 +1,4 @@ MAJOR=44 MINOR=0 -BUILD=2377 +BUILD=2379 PATCH=0
diff --git a/chrome/android/java/res/layout/fre_spinner_dropdown.xml b/chrome/android/java/res/layout/fre_spinner_dropdown.xml index 0358e79..ba06f4ee 100644 --- a/chrome/android/java/res/layout/fre_spinner_dropdown.xml +++ b/chrome/android/java/res/layout/fre_spinner_dropdown.xml
@@ -14,4 +14,4 @@ android:layout_height="wrap_content" android:textColor="@color/fre_text_color" android:textSize="@dimen/fre_normal_text_size" - android:padding="@dimen/fre_button_padding" /> \ No newline at end of file + android:padding="12dp" /> \ No newline at end of file
diff --git a/chrome/android/java/res/layout/fre_spinner_text.xml b/chrome/android/java/res/layout/fre_spinner_text.xml index c64afaa1..cb14ff1 100644 --- a/chrome/android/java/res/layout/fre_spinner_text.xml +++ b/chrome/android/java/res/layout/fre_spinner_text.xml
@@ -10,7 +10,6 @@ android:layout_height="wrap_content" android:maxWidth="216dp" android:layoutDirection="locale" - android:padding="@dimen/fre_button_padding" android:paddingBottom="16dp" android:paddingEnd="12dp" android:paddingStart="12dp"
diff --git a/chrome/android/java/res/values/dimens.xml b/chrome/android/java/res/values/dimens.xml index 04eab23f..739e71b 100644 --- a/chrome/android/java/res/values/dimens.xml +++ b/chrome/android/java/res/values/dimens.xml
@@ -113,7 +113,6 @@ <dimen name="fre_margin">24dp</dimen> <dimen name="fre_title_text_size">24sp</dimen> <dimen name="fre_button_text_size">14sp</dimen> - <dimen name="fre_button_padding">12dp</dimen> <dimen name="fre_normal_text_size">14sp</dimen> <dimen name="fre_image_carousel_width">260dp</dimen> <dimen name="fre_image_carousel_height">130dp</dimen>
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeContentViewClient.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeContentViewClient.java deleted file mode 100644 index 569fc724..0000000 --- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeContentViewClient.java +++ /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. - -package org.chromium.chrome.browser; - -import org.chromium.chrome.browser.preferences.PrefServiceBridge; -import org.chromium.content.browser.ContentViewClient; - -/** - * The default {@link ContentViewClient} implementation for the chrome layer embedders. - */ -public class ChromeContentViewClient extends ContentViewClient { - - @Override - public boolean isJavascriptEnabled() { - return PrefServiceBridge.getInstance().javaScriptEnabled(); - } - -}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/Tab.java b/chrome/android/java/src/org/chromium/chrome/browser/Tab.java index 1cec7a3..db202ccd 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/Tab.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/Tab.java
@@ -476,7 +476,7 @@ * ContentViewClient that provides basic tab functionality and is meant to be extended * by child classes. */ - protected class TabContentViewClient extends ChromeContentViewClient { + protected class TabContentViewClient extends ContentViewClient { @Override public void onUpdateTitle(String title) { updateTitle(title); @@ -1488,10 +1488,6 @@ updateTitle(); removeSadTabIfPresent(); - if (getContentViewCore() != null) { - getContentViewCore().stopCurrentAccessibilityNotifications(); - } - clearHungRendererState(); for (TabObserver observer : mObservers) observer.onPageLoadStarted(this); @@ -1547,7 +1543,7 @@ */ protected void initContentViewCore(WebContents webContents) { ContentViewCore cvc = new ContentViewCore(mContext); - ContentView cv = ContentView.newInstance(mContext, cvc); + ContentView cv = new ContentView(mContext, cvc); cv.setContentDescription(mContext.getResources().getString( R.string.accessibility_content_view)); cvc.initialize(cv, cv, webContents, getWindowAndroid()); @@ -2165,7 +2161,7 @@ private void swapWebContents( WebContents webContents, boolean didStartLoad, boolean didFinishLoad) { ContentViewCore cvc = new ContentViewCore(mContext); - ContentView cv = ContentView.newInstance(mContext, cvc); + ContentView cv = new ContentView(mContext, cvc); cv.setContentDescription(mContext.getResources().getString( R.string.accessibility_content_view)); cvc.initialize(cv, cv, webContents, getWindowAndroid());
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/TtsPlatformImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/TtsPlatformImpl.java index 7e6a556..f7c8731 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/TtsPlatformImpl.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/TtsPlatformImpl.java
@@ -99,11 +99,9 @@ Context context) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { return new LollipopTtsPlatformImpl(nativeTtsPlatformImplAndroid, context); - } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) { + } else { return new TtsPlatformImpl(nativeTtsPlatformImplAndroid, context); } - - return null; } /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/document/DocumentMetricIds.java b/chrome/android/java/src/org/chromium/chrome/browser/document/DocumentMetricIds.java index 9ede832..ca5c6d9c 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/document/DocumentMetricIds.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/document/DocumentMetricIds.java
@@ -34,6 +34,7 @@ public static final int STARTED_BY_EXTERNAL_APP_NEWS = 408; public static final int STARTED_BY_EXTERNAL_APP_LINE = 409; public static final int STARTED_BY_EXTERNAL_APP_WHATSAPP = 410; + public static final int STARTED_BY_EXTERNAL_APP_GSA = 411; public static final int STARTED_BY_CONTEXTUAL_SEARCH = 500; // DocumentActivity.OptOutDecision (enumerated)
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/ChromeDownloadDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/download/ChromeDownloadDelegate.java index c76adbd..df8d40a 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/download/ChromeDownloadDelegate.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/download/ChromeDownloadDelegate.java
@@ -4,11 +4,8 @@ package org.chromium.chrome.browser.download; -import android.content.ActivityNotFoundException; import android.content.Context; import android.content.Intent; -import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; import android.net.Uri; import android.os.AsyncTask; import android.os.Environment; @@ -31,7 +28,6 @@ import org.chromium.content_public.browser.WebContents; import java.io.File; -import java.util.List; /** * Chrome implementation of the ContentViewDownloadDelegate interface. @@ -131,45 +127,10 @@ // Query the package manager to see if there's a registered handler that matches. Intent intent = new Intent(Intent.ACTION_VIEW); intent.setDataAndType(Uri.parse(downloadInfo.getUrl()), downloadInfo.getMimeType()); - ResolveInfo info = mContext.getPackageManager().resolveActivity(intent, - PackageManager.MATCH_DEFAULT_ONLY); - boolean activityResolved = false; - if (info != null) { - final String packageName = mContext.getPackageName(); - if (info.match != 0) { - // If we resolved to ourselves, we don't want to attempt to load the url only to - // try and download it again. - if (!packageName.equals(info.activityInfo.packageName)) { - // Someone (other than us) knows how to handle this mime type with this - // scheme, don't download. - activityResolved = true; - } - } else { - // If we resolved to ResolverActivity, we should check if Chrome can be one of - // options. If so, we don't want to show an intent picker. - List<ResolveInfo> handlers = mContext.getPackageManager().queryIntentActivities( - intent, PackageManager.MATCH_DEFAULT_ONLY); - if (handlers != null && !handlers.isEmpty()) { - activityResolved = true; - for (ResolveInfo resolveInfo : handlers) { - if (packageName.equals(resolveInfo.activityInfo.packageName)) { - activityResolved = false; - break; - } - } - } - } - - if (activityResolved) { - try { - mContext.startActivity(intent); - return; - } catch (ActivityNotFoundException ex) { - Log.d(LOGTAG, "activity not found for " + downloadInfo.getMimeType() - + " over " + Uri.parse(downloadInfo.getUrl()).getScheme(), ex); - // Best behavior is to fall back to a download in this case. - } - } + // If the intent is resolved to ourselves, we don't want to attempt to load the url + // only to try and download it again. + if (DownloadManagerService.openIntent(mContext, intent, false)) { + return; } } onDownloadStartNoStream(downloadInfo);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadManagerService.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadManagerService.java index 75e1f05b..ffcd4d5 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadManagerService.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadManagerService.java
@@ -11,6 +11,8 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; import android.database.Cursor; import android.net.Uri; import android.os.AsyncTask; @@ -31,6 +33,7 @@ import java.io.File; import java.util.HashSet; +import java.util.List; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicBoolean; @@ -46,6 +49,7 @@ private static final String DOWNLOAD_NOTIFICATION_IDS = "DownloadNotificationIds"; private static final String DOWNLOAD_DIRECTORY = "Download"; protected static final String PENDING_OMA_DOWNLOADS = "PendingOMADownloads"; + private static final String PDF_VIEWER = "com.google.android.apps.docs"; private static final long UPDATE_DELAY_MILLIS = 1000; private static DownloadManagerService sDownloadManagerService; @@ -536,26 +540,20 @@ DownloadInfo info = mPendingAutoOpenDownloads.get(downloadId); switch (status) { case DownloadManager.STATUS_SUCCESSFUL: - try { - mPendingAutoOpenDownloads.remove(downloadId); - if (OMADownloadHandler.OMA_DOWNLOAD_DESCRIPTOR_MIME.equalsIgnoreCase( - info.getMimeType())) { - mOMADownloadHandler.handleOMADownload( - info, downloadId); - manager.remove(downloadId); - break; - } - Uri uri = manager.getUriForDownloadedFile(downloadId); - Intent launchIntent = new Intent(Intent.ACTION_VIEW); - - launchIntent.setDataAndType( - uri, manager.getMimeTypeForDownloadedFile(downloadId)); - launchIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - - mContext.startActivity(launchIntent); - } catch (ActivityNotFoundException e) { - Log.w(TAG, "Activity not found."); + mPendingAutoOpenDownloads.remove(downloadId); + if (OMADownloadHandler.OMA_DOWNLOAD_DESCRIPTOR_MIME.equalsIgnoreCase( + info.getMimeType())) { + mOMADownloadHandler.handleOMADownload( + info, downloadId); + manager.remove(downloadId); + break; } + Uri uri = manager.getUriForDownloadedFile(downloadId); + Intent launchIntent = new Intent(Intent.ACTION_VIEW); + launchIntent.setDataAndType( + uri, manager.getMimeTypeForDownloadedFile(downloadId)); + launchIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + openIntent(mContext, launchIntent, true); break; case DownloadManager.STATUS_FAILED: mPendingAutoOpenDownloads.remove(downloadId); @@ -729,4 +727,63 @@ return contentDisposition != null && contentDisposition.regionMatches(true, 0, "attachment", 0, 10); } + + /** + * Launch the best activity for the given intent. If a default activity is provided, + * choose the default one. Otherwise, return the Intent picker if there are more than one + * capable activities. If the intent is pdf type, return the platform pdf viewer if + * it is available so user don't need to choose it from Intent picker. + * + * @param context Context of the app. + * @param intent Intent to open. + * @param allowSelfOpen Whether chrome itself is allowed to open the intent. + * @return true if an Intent is launched, or false otherwise. + */ + public static boolean openIntent(Context context, Intent intent, boolean allowSelfOpen) { + boolean activityResolved = false; + ResolveInfo info = context.getPackageManager().resolveActivity(intent, + PackageManager.MATCH_DEFAULT_ONLY); + if (info != null) { + final String packageName = context.getPackageName(); + if (info.match != 0) { + if (allowSelfOpen || !packageName.equals(info.activityInfo.packageName)) { + activityResolved = true; + } + } else { + // If we resolved to ResolverActivity, we should check if Chrome can be one of + // options. If so, we don't want to show an intent picker in case |allowSelfOpen| + // is false, unless plaform pdf viewer is one of the option. + List<ResolveInfo> handlers = context.getPackageManager().queryIntentActivities( + intent, PackageManager.MATCH_DEFAULT_ONLY); + if (handlers != null && !handlers.isEmpty()) { + activityResolved = true; + boolean canSelfOpen = false; + boolean hasPdfViewer = false; + for (ResolveInfo resolveInfo : handlers) { + String pName = resolveInfo.activityInfo.packageName; + if (packageName.equals(pName)) { + canSelfOpen = true; + } else if (PDF_VIEWER.equals(pName)) { + intent.setClassName(pName, resolveInfo.activityInfo.name); + hasPdfViewer = true; + break; + } + } + if ((canSelfOpen && !allowSelfOpen) && !hasPdfViewer) { + activityResolved = false; + } + } + } + } + if (activityResolved) { + try { + context.startActivity(intent); + return true; + } catch (ActivityNotFoundException ex) { + Log.d(TAG, "activity not found for " + intent.getType() + + " over " + intent.getData().getScheme(), ex); + } + } + return false; + } }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/externalnav/ExternalNavigationHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/externalnav/ExternalNavigationHandler.java index a4a940c..e03416e 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/externalnav/ExternalNavigationHandler.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/externalnav/ExternalNavigationHandler.java
@@ -4,13 +4,11 @@ package org.chromium.chrome.browser.externalnav; -import android.annotation.TargetApi; import android.app.Activity; import android.content.ActivityNotFoundException; import android.content.ComponentName; import android.content.Intent; import android.net.Uri; -import android.os.Build; import android.provider.Browser; import android.util.Log; import android.webkit.WebView; @@ -106,7 +104,6 @@ return result; } - @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) private OverrideUrlLoadingResult shouldOverrideUrlLoadingInternal( ExternalNavigationParams params, Intent intent, boolean hasBrowserFallbackUrl, String browserFallbackUrl) { @@ -254,13 +251,12 @@ // security (only access to BROWSABLE activities). intent.addCategory(Intent.CATEGORY_BROWSABLE); intent.setComponent(null); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) { - Intent selector = intent.getSelector(); - if (selector != null) { - selector.addCategory(Intent.CATEGORY_BROWSABLE); - selector.setComponent(null); - } + Intent selector = intent.getSelector(); + if (selector != null) { + selector.addCategory(Intent.CATEGORY_BROWSABLE); + selector.setComponent(null); } + // Set the Browser application ID to us in case the user chooses Chrome // as the app. This will make sure the link is opened in the same tab // instead of making a new one.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/remote/TransportControl.java b/chrome/android/java/src/org/chromium/chrome/browser/media/remote/TransportControl.java index 7cdf6373..77b74ec 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/media/remote/TransportControl.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/media/remote/TransportControl.java
@@ -32,7 +32,9 @@ void onStop(); } - private final Set<Listener> mListeners = new CopyOnWriteArraySet<Listener>(); + // Initialized lazily to simplify testing. Should only ever be accessed through getListeners to + // ensure correct initialization. + private Set<Listener> mListeners; private String mScreenName; private String mError; protected RemoteVideoInfo mVideoInfo; @@ -49,11 +51,11 @@ * Sets the name to display for the device on the TransportControl. */ public final void setScreenName(String screenName) { - if (TextUtils.equals(this.mScreenName, screenName)) { + if (TextUtils.equals(mScreenName, screenName)) { return; } - this.mScreenName = screenName; + mScreenName = screenName; onScreenNameChanged(); } @@ -71,11 +73,12 @@ * {@link #clearError()} */ public final void setError(String message) { - if (TextUtils.equals(mError, message)) { + String newError = TextUtils.isEmpty(message) ? null : message; + if (TextUtils.equals(mError, newError)) { return; } - mError = TextUtils.isEmpty(message) ? null : message; + mError = newError; onErrorChanged(); } @@ -107,11 +110,11 @@ * @param videoInfo the video information to use. */ public final void setVideoInfo(RemoteVideoInfo videoInfo) { - if (equal(this.mVideoInfo, videoInfo)) { + if (equal(mVideoInfo, videoInfo)) { return; } - this.mVideoInfo = videoInfo; + mVideoInfo = videoInfo; onVideoInfoChanged(); } @@ -127,11 +130,13 @@ * Sets the poster bitmap to display on the TransportControl. */ public final void setPosterBitmap(Bitmap posterBitmap) { - if (equal(this.mPosterBitmap, posterBitmap)) { + // Note that equality of bitmaps is simply an object comparison, so a copy will be treated + // as a new bitmap + if (equal(mPosterBitmap, posterBitmap)) { return; } - this.mPosterBitmap = posterBitmap; + mPosterBitmap = posterBitmap; onPosterBitmapChanged(); } @@ -140,7 +145,7 @@ * @param listener the Listener to be registered. */ public void addListener(Listener listener) { - mListeners.add(listener); + getListeners().add(listener); } /** @@ -148,7 +153,7 @@ * @param listener the Listener to be removed. */ public void removeListener(Listener listener) { - mListeners.remove(listener); + getListeners().remove(listener); } /** @@ -171,6 +176,7 @@ * @return the current list of listeners. */ protected final Set<Listener> getListeners() { + if (mListeners == null) mListeners = new CopyOnWriteArraySet<Listener>(); return mListeners; }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationUIManager.java b/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationUIManager.java index 476649c..602d354 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationUIManager.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationUIManager.java
@@ -15,7 +15,10 @@ import android.net.Uri; import android.os.Bundle; import android.support.v4.app.NotificationCompat; +import android.text.Spannable; +import android.text.SpannableStringBuilder; import android.text.TextUtils; +import android.text.style.StyleSpan; import android.util.Log; import org.chromium.base.CalledByNative; @@ -345,6 +348,7 @@ .addAction(R.drawable.settings_cog, res.getString(R.string.page_info_site_settings_button), pendingSettingsIntent) + .setTicker(createTickerText(title, body)) .setSubText(origin); // Use the system's default ringtone, vibration and indicator lights unless the notification @@ -356,6 +360,28 @@ } /** + * Creates the ticker text for a notification having |title| and |body|. The notification's + * title will be printed in bold, followed by the text of the body. + * + * @param title Title of the notification. + * @param body Textual contents of the notification. + * @return A character sequence containing the ticker's text. + */ + private CharSequence createTickerText(String title, String body) { + SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder(); + + spannableStringBuilder.append(title); + spannableStringBuilder.append("\n"); + spannableStringBuilder.append(body); + + // Mark the title of the notification as being bold. + spannableStringBuilder.setSpan(new StyleSpan(android.graphics.Typeface.BOLD), + 0, title.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE); + + return spannableStringBuilder; + } + + /** * Ensures the existance of an icon generator, which is created lazily. * * @return The icon generator which can be used.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/TransitionPageHelper.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/TransitionPageHelper.java index 4f124de8..e97f8b5 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/tab/TransitionPageHelper.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/TransitionPageHelper.java
@@ -24,11 +24,11 @@ import org.chromium.base.ApplicationStatus.ActivityStateListener; import org.chromium.base.CalledByNative; import org.chromium.base.FieldTrialList; -import org.chromium.chrome.browser.ChromeContentViewClient; import org.chromium.chrome.browser.ContentViewUtil; import org.chromium.chrome.browser.EmptyTabObserver; import org.chromium.chrome.browser.Tab; import org.chromium.content.browser.ContentView; +import org.chromium.content.browser.ContentViewClient; import org.chromium.content.browser.ContentViewCore; import org.chromium.content_public.browser.JavaScriptCallback; import org.chromium.content_public.browser.LoadUrlParams; @@ -600,7 +600,7 @@ if (mNativeTransitionPageHelperPtr == 0) return; mTransitionContentViewCore = new ContentViewCore(mContext); - ContentView cv = ContentView.newInstance(mContext, mTransitionContentViewCore); + ContentView cv = new ContentView(mContext, mTransitionContentViewCore); mTransitionContentViewCore.initialize(cv, cv, ContentViewUtil.createWebContentsWithSharedSiteInstance(mSourceContentViewCore), mWindowAndroid); @@ -617,7 +617,7 @@ nativeSetWebContents(mNativeTransitionPageHelperPtr, mTransitionContentViewCore); setTransitionOpacity(0.0f); - mTransitionContentViewCore.setContentViewClient(new ChromeContentViewClient() { + mTransitionContentViewCore.setContentViewClient(new ContentViewClient() { @Override public void onOffsetsForFullscreenChanged( float topControlsOffsetYPix,
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/InterceptNavigationDelegateTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/InterceptNavigationDelegateTest.java index 7d77bec..7980c4d 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/InterceptNavigationDelegateTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/InterceptNavigationDelegateTest.java
@@ -34,6 +34,8 @@ BASE_URL + "navigation_from_xhr_callback_and_short_timeout.html"; private static final String NAVIGATION_FROM_XHR_CALLBACK_AND_LONG_TIMEOUT_PAGE = BASE_URL + "navigation_from_xhr_callback_and_long_timeout.html"; + private static final String NAVIGATION_FROM_IMAGE_ONLOAD_PAGE = + BASE_URL + "navigation_from_image_onload.html"; private static final long DEFAULT_MAX_TIME_TO_WAIT_IN_MS = 3000; private static final long LONG_MAX_TIME_TO_WAIT_IN_MS = 20000; @@ -136,4 +138,15 @@ assertEquals(false, mHistory.get(1).hasUserGesture); assertEquals(false, mHistory.get(1).hasUserGestureCarryover); } + + @SmallTest + public void testNavigationFromImageOnLoad() throws InterruptedException { + loadUrlWithSanitization(TestHttpServerClient.getUrl(NAVIGATION_FROM_IMAGE_ONLOAD_PAGE)); + assertEquals(1, mHistory.size()); + + TouchCommon.singleClickView(mActivity.getActiveTab().getView(), 25, 25); + waitTillExpectedCallsComplete(2, DEFAULT_MAX_TIME_TO_WAIT_IN_MS); + assertEquals(false, mHistory.get(1).hasUserGesture); + assertEquals(true, mHistory.get(1).hasUserGestureCarryover); + } } \ No newline at end of file
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/externalnav/ExternalNavigationHandlerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/externalnav/ExternalNavigationHandlerTest.java index 42426a60..c44b6c8 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/externalnav/ExternalNavigationHandlerTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/externalnav/ExternalNavigationHandlerTest.java
@@ -4,7 +4,6 @@ package org.chromium.chrome.browser.externalnav; -import android.annotation.TargetApi; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -12,7 +11,6 @@ import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.net.Uri; -import android.os.Build; import android.os.SystemClock; import android.provider.Browser; import android.test.InstrumentationTestCase; @@ -983,7 +981,6 @@ intent.getComponent()); } - @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) public void check(String url, String referrerUrl, boolean isIncognito, @@ -1015,11 +1012,9 @@ if (startActivityCalled && expectSaneIntent) { checkIntentSanity(mDelegate.startActivityIntent, "Intent"); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) { - if (mDelegate.startActivityIntent.getSelector() != null) { - checkIntentSanity(mDelegate.startActivityIntent.getSelector(), - "Intent's selector"); - } + if (mDelegate.startActivityIntent.getSelector() != null) { + checkIntentSanity(mDelegate.startActivityIntent.getSelector(), + "Intent's selector"); } } }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/input/SelectPopupOtherContentViewTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/input/SelectPopupOtherContentViewTest.java index 306b11c..2989bfd 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/input/SelectPopupOtherContentViewTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/input/SelectPopupOtherContentViewTest.java
@@ -77,7 +77,7 @@ WindowAndroid windowAndroid = new ActivityWindowAndroid(getActivity()); ContentViewCore contentViewCore = new ContentViewCore(getActivity()); - ContentView cv = ContentView.newInstance(getActivity(), contentViewCore); + ContentView cv = new ContentView(getActivity(), contentViewCore); contentViewCore.initialize(cv, cv, webContents, windowAndroid); contentViewCore.destroy(); }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/notifications/NotificationUIManagerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/notifications/NotificationUIManagerTest.java index 39b2634..7e14a0c5 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/notifications/NotificationUIManagerTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/notifications/NotificationUIManagerTest.java
@@ -4,6 +4,8 @@ package org.chromium.chrome.browser.notifications; +import static org.chromium.base.test.util.ScalableTimeout.scaleTimeout; + import android.app.Notification; import android.graphics.Bitmap; import android.os.Build; @@ -39,6 +41,12 @@ private static final String NOTIFICATION_TEST_PAGE = TestHttpServerClient.getUrl("chrome/test/data/notifications/android_test.html"); + /** The maximum time to wait for a criteria to become valid. */ + private static final long MAX_TIME_TO_POLL_MS = scaleTimeout(6000); + + /** The polling interval to wait between checking for a satisfied criteria. */ + private static final long POLLING_INTERVAL_MS = 50; + private MockNotificationManagerProxy mMockNotificationManager; /** @@ -117,7 +125,7 @@ public boolean isSatisfied() { return mMockNotificationManager.getMutationCountAndDecrement() > 0; } - }); + }, MAX_TIME_TO_POLL_MS, POLLING_INTERVAL_MS); } @Override @@ -154,6 +162,12 @@ assertEquals("Hello", notification.extras.getString(Notification.EXTRA_TEXT)); assertEquals(getOrigin(), notification.extras.getString(Notification.EXTRA_SUB_TEXT)); + // Verify that the ticker text contains the notification's title and body. + String tickerText = notification.tickerText.toString(); + + assertTrue(tickerText.contains("MyNotification")); + assertTrue(tickerText.contains("Hello")); + // Validate the appearance style of the notification. The EXTRA_TEMPLATE was inroduced // in Android Lollipop, we cannot verify this in earlier versions. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/omaha/ResponseParserTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/omaha/ResponseParserTest.java deleted file mode 100644 index 6ad87a99..0000000 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/omaha/ResponseParserTest.java +++ /dev/null
@@ -1,362 +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.chrome.browser.omaha; - -import android.test.InstrumentationTestCase; -import android.test.suitebuilder.annotation.SmallTest; -import android.util.Xml; - -import org.chromium.base.test.util.Feature; -import org.xmlpull.v1.XmlSerializer; - -import java.io.IOException; -import java.io.StringWriter; - -/** - * Unit tests for the Omaha ResponseParser. - */ -public class ResponseParserTest extends InstrumentationTestCase { - // Note that the Omaha server appends "/" to the end of the URL codebase. - private static final String STRIPPED_URL = - "https://play.google.com/store/apps/details?id=com.google.android.apps.chrome"; - private static final String URL = STRIPPED_URL + "/"; - private static final String NEXT_VERSION = "1.2.3.4"; - - private static final String APP_STATUS_OK = "ok"; - private static final String APP_STATUS_RESTRICTED = "restricted"; - private static final String APP_STATUS_ERROR = "error-whatever-else"; - - private static final String UPDATE_STATUS_OK = "ok"; - private static final String UPDATE_STATUS_NOUPDATE = "noupdate"; - private static final String UPDATE_STATUS_ERROR = "error-osnotsupported"; - private static final String UPDATE_STATUS_WTF = "omgwtfbbq"; - - /** - * Create XML for testing. - * @param xmlProtocol Version number of the protocol. Expected to be "3.0" for valid XML. - * @param elapsedSeconds Number of seconds since server midnight. - * @param appStatus Status to use for the <app> element. - * @param addInstall Whether or not to add an install event. - * @param addPing Whether or not to add a ping event. - * @param updateStatus Status to use for the <updatecheck> element. - * @return The completed XML. - */ - private static String createTestXML(String xmlProtocol, String elapsedSeconds, - String appStatus, boolean addInstall, boolean addPing, String updateStatus, - String updateUrl) { - StringWriter writer = new StringWriter(); - try { - XmlSerializer serializer = Xml.newSerializer(); - serializer.setOutput(writer); - serializer.startDocument("UTF-8", true); - - // Set up <response ...> - serializer.startTag(null, "response"); - serializer.attribute(null, "server", "prod"); - if (xmlProtocol != null) { - serializer.attribute(null, "protocol", xmlProtocol); - } - - // Create <daystart> element. - if (elapsedSeconds != null) { - serializer.startTag(null, "daystart"); - serializer.attribute(null, "elapsed_seconds", elapsedSeconds); - serializer.endTag(null, "daystart"); - } - - // Create <app> element with unused attribute. - serializer.startTag(null, "app"); - serializer.attribute(null, "appid", "{APP_ID}"); - serializer.attribute(null, "status", appStatus); - serializer.attribute(null, "unused", "attribute"); - - if (addInstall) { - serializer.startTag(null, "event"); - serializer.attribute(null, "status", "ok"); - serializer.endTag(null, "event"); - } - - if (addPing) { - serializer.startTag(null, "ping"); - serializer.attribute(null, "status", "ok"); - serializer.endTag(null, "ping"); - } - - if (updateStatus != null) { - serializer.startTag(null, "updatecheck"); - serializer.attribute(null, "status", updateStatus); - if (UPDATE_STATUS_OK.equals(updateStatus)) { - createUpdateXML(serializer, updateUrl); - } - serializer.endTag(null, "updatecheck"); - } - serializer.endTag(null, "app"); - - // Create extraneous tag. - serializer.startTag(null, "extraneous"); - serializer.attribute(null, "useless", "yes"); - serializer.endTag(null, "extraneous"); - - serializer.endTag(null, "response"); - serializer.endDocument(); - } catch (IOException e) { - fail("Caught an IOException creating the XML: " + e); - } catch (IllegalArgumentException e) { - fail("Caught an IllegalArgumentException creating the XML: " + e); - } catch (IllegalStateException e) { - fail("Caught an IllegalStateException creating the XML: " + e); - } - - return writer.toString(); - } - - private static void createUpdateXML(XmlSerializer serializer, String updateUrl) - throws IOException { - // End result should look something like: - // <updatecheck status="ok"> - // <urls> - // <url codebase="URL" /> - // </urls> - // <manifest garbage="attribute" version="NEXT_VERSION"> - // <packages> - // <package hash="0" name="dummy.apk" required="true" size="0" /> - // </packages> - // <actions> - // <action event="install" run="dummy.apk" /> - // <action event="postinstall" /> - // </actions> - // </manifest> - // <better be="ignored" /> - //</updatecheck> - - // Create <urls> and its descendants. - serializer.startTag(null, "urls"); - if (updateUrl != null) { - serializer.startTag(null, "url"); - serializer.attribute(null, "codebase", updateUrl); - serializer.endTag(null, "url"); - } - serializer.endTag(null, "urls"); - - // Create <manifest> and its descendants. - serializer.startTag(null, "manifest"); - serializer.attribute(null, "garbage", "attribute"); - serializer.attribute(null, "version", NEXT_VERSION); - - // Create <packages> and its children. - serializer.startTag(null, "packages"); - serializer.startTag(null, "package"); - serializer.attribute(null, "hash", "0"); - serializer.attribute(null, "name", "dummy.apk"); - serializer.attribute(null, "required", "true"); - serializer.attribute(null, "size", "0"); - serializer.endTag(null, "package"); - serializer.endTag(null, "packages"); - - // Create <actions> and its children. - serializer.startTag(null, "actions"); - serializer.startTag(null, "action"); - serializer.attribute(null, "event", "install"); - serializer.attribute(null, "run", "dummy.apk"); - serializer.endTag(null, "action"); - - serializer.startTag(null, "action"); - serializer.attribute(null, "event", "postinstall"); - serializer.endTag(null, "action"); - serializer.endTag(null, "actions"); - serializer.endTag(null, "manifest"); - - // Create a dummy element for testing to make sure it's ignored. - serializer.startTag(null, "dummy"); - serializer.attribute(null, "hopefully", "ignored"); - serializer.endTag(null, "dummy"); - } - - /** - * Runs a test that is expected to pass. - * @param appStatus Status to use for the <app> element. - * @param addInstall Whether or not to add an install event. - * @param addPing Whether or not to add a ping. - * @param updateStatus Status to use for the <updatecheck> element. - * @throws RequestFailureException Thrown if the test fails. - */ - private static void runSuccessTest(String appStatus, boolean addInstall, boolean addPing, - String updateStatus) throws RequestFailureException { - String xml = - createTestXML("3.0", "12345", appStatus, addInstall, addPing, updateStatus, URL); - ResponseParser parser = - new ResponseParser(true, "{APP_ID}", addInstall, addPing, updateStatus != null); - parser.parseResponse(xml); - - assertEquals("elapsed_seconds doesn't match.", 12345, parser.getDaystartSeconds()); - assertEquals("<app> status doesn't match.", appStatus, parser.getAppStatus()); - assertEquals("<updatecheck> status doesn't match.", updateStatus, parser.getUpdateStatus()); - if (UPDATE_STATUS_OK.equals(updateStatus)) { - assertEquals("Version number doesn't match.", "1.2.3.4", parser.getNewVersion()); - assertEquals("Market URL doesn't match.", STRIPPED_URL, parser.getURL()); - } else { - assertEquals("Version number doesn't match.", null, parser.getNewVersion()); - assertEquals("Market URL doesn't match.", null, parser.getURL()); - } - } - - /** - * Runs a test that is expected to fail in a particular way. - * @param xml XML to parse. - * @param expectedErrorCode Expected error code. - * @param expectInstall Whether or not the parser should expect an install event. - * @param expectPing Whether or not the parser should expect a ping element. - * @param expectUpdate Whether or not the parser should expect an update check. - */ - private static void runFailureTest(String xml, int expectedErrorCode, - boolean expectInstall, boolean expectPing, boolean expectUpdate) { - ResponseParser parser = - new ResponseParser(true, "{APP_ID}", expectInstall, expectPing, expectUpdate); - - try { - parser.parseResponse(xml); - } catch (RequestFailureException e) { - assertEquals("Incorrect error code received.", expectedErrorCode, e.errorCode); - return; - } - - fail("Failed to throw RequestFailureException for bad XML."); - } - - @SmallTest - @Feature({"Omaha"}) - public void testValidAllTypes() throws RequestFailureException { - runSuccessTest(APP_STATUS_OK, true, true, UPDATE_STATUS_OK); - } - - @SmallTest - @Feature({"Omaha"}) - public void testValidNoInstall() throws RequestFailureException { - runSuccessTest(APP_STATUS_OK, false, true, UPDATE_STATUS_OK); - } - - @SmallTest - @Feature({"Omaha"}) - public void testValidNoPing() throws RequestFailureException { - runSuccessTest(APP_STATUS_OK, true, false, UPDATE_STATUS_OK); - } - - @SmallTest - @Feature({"Omaha"}) - public void testValidNoUpdatecheck() throws RequestFailureException { - runSuccessTest(APP_STATUS_OK, true, true, null); - } - - @SmallTest - @Feature({"Omaha"}) - public void testValidUpdatecheckNoUpdate() throws RequestFailureException { - runSuccessTest(APP_STATUS_OK, false, false, UPDATE_STATUS_NOUPDATE); - } - - @SmallTest - @Feature({"Omaha"}) - public void testValidUpdatecheckError() throws RequestFailureException { - runSuccessTest(APP_STATUS_OK, false, false, UPDATE_STATUS_ERROR); - } - - @SmallTest - @Feature({"Omaha"}) - public void testValidUpdatecheckUnknown() throws RequestFailureException { - runSuccessTest(APP_STATUS_OK, false, false, UPDATE_STATUS_WTF); - } - - @SmallTest - @Feature({"Omaha"}) - public void testValidAppStatusRestricted() throws RequestFailureException { - runSuccessTest(APP_STATUS_RESTRICTED, false, false, null); - } - - @SmallTest - @Feature({"Omaha"}) - public void testFailBogusResponse() { - String xml = "Bogus"; - runFailureTest(xml, RequestFailureException.ERROR_MALFORMED_XML, false, false, false); - } - - @SmallTest - @Feature({"Omaha"}) - public void testBadResponseProtocol() { - String xml = - createTestXML("2.0", "12345", APP_STATUS_OK, false, false, UPDATE_STATUS_OK, URL); - runFailureTest(xml, RequestFailureException.ERROR_PARSE_RESPONSE, false, false, false); - } - - @SmallTest - @Feature({"Omaha"}) - public void testFailMissingDaystart() { - String xml = - createTestXML("3.0", null, APP_STATUS_OK, false, false, UPDATE_STATUS_OK, URL); - runFailureTest(xml, RequestFailureException.ERROR_PARSE_DAYSTART, false, false, true); - } - - @SmallTest - @Feature({"Omaha"}) - public void testAppTagMissingUpdatecheck() { - String xml = - createTestXML("3.0", "12345", APP_STATUS_OK, true, false, null, URL); - runFailureTest(xml, RequestFailureException.ERROR_PARSE_UPDATECHECK, true, false, true); - } - - @SmallTest - @Feature({"Omaha"}) - public void testAppTagUnexpectedUpdatecheck() { - String xml = - createTestXML("3.0", "12345", APP_STATUS_OK, true, false, UPDATE_STATUS_OK, URL); - runFailureTest(xml, RequestFailureException.ERROR_PARSE_UPDATECHECK, true, false, false); - } - - @SmallTest - @Feature({"Omaha"}) - public void testAppTagMissingPing() { - String xml = - createTestXML("3.0", "12345", APP_STATUS_OK, false, false, UPDATE_STATUS_OK, URL); - runFailureTest(xml, RequestFailureException.ERROR_PARSE_PING, false, true, true); - } - - @SmallTest - @Feature({"Omaha"}) - public void testAppTagUnexpectedPing() { - String xml = - createTestXML("3.0", "12345", APP_STATUS_OK, false, true, UPDATE_STATUS_OK, URL); - runFailureTest(xml, RequestFailureException.ERROR_PARSE_PING, false, false, true); - } - - @SmallTest - @Feature({"Omaha"}) - public void testAppTagMissingInstall() { - String xml = - createTestXML("3.0", "12345", APP_STATUS_OK, false, false, UPDATE_STATUS_OK, URL); - runFailureTest(xml, RequestFailureException.ERROR_PARSE_EVENT, true, false, true); - } - - @SmallTest - @Feature({"Omaha"}) - public void testAppTagUnexpectedInstall() { - String xml = - createTestXML("3.0", "12345", APP_STATUS_OK, true, false, UPDATE_STATUS_OK, URL); - runFailureTest(xml, RequestFailureException.ERROR_PARSE_EVENT, false, false, true); - } - - @SmallTest - @Feature({"Omaha"}) - public void testAppTagStatusError() { - String xml = - createTestXML("3.0", "12345", APP_STATUS_ERROR, false, false, null, URL); - runFailureTest(xml, RequestFailureException.ERROR_PARSE_APP, false, false, false); - } - - @SmallTest - @Feature({"Omaha"}) - public void testUpdatecheckMissingUrl() { - String xml = createTestXML( - "3.0", "12345", APP_STATUS_OK, false, false, UPDATE_STATUS_OK, null); - runFailureTest(xml, RequestFailureException.ERROR_PARSE_URLS, false, false, true); - } -}
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/media/remote/TransportControlTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/media/remote/TransportControlTest.java new file mode 100644 index 0000000..d47a1c8 --- /dev/null +++ b/chrome/android/junit/src/org/chromium/chrome/browser/media/remote/TransportControlTest.java
@@ -0,0 +1,198 @@ +// 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.media.remote; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.nullValue; +import static org.junit.Assert.assertThat; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import android.graphics.Bitmap; +import android.graphics.Color; + +import org.chromium.base.test.util.Feature; +import org.chromium.testing.local.LocalRobolectricTestRunner; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.robolectric.annotation.Config; + +/** + * Robolectric tests of TransportControl class + */ +@RunWith(LocalRobolectricTestRunner.class) +@Config(manifest = Config.NONE) +public class TransportControlTest { + + TransportControl mTransportControl; + + @Before + public void setUp() { + mTransportControl = Mockito.mock(TransportControl.class, Mockito.CALLS_REAL_METHODS); + } + + /** + * Test method for {@link TransportControl#setScreenName} . + */ + @Test + @Feature("MediaRemote") + public void testSetScreenName() { + mTransportControl.setScreenName("Name1"); + assertThat("getScreenName should return the last screen name set", + mTransportControl.getScreenName(), equalTo("Name1")); + // Check that onScreenNameChanged is called precisely once + verify(mTransportControl).onScreenNameChanged(); + // Try setting the same screen name again + mTransportControl.setScreenName("Name1"); + // There should still have only been one call to onScreenNameChanged + verify(mTransportControl).onScreenNameChanged(); + // And try a different screen name + mTransportControl.setScreenName("Name2"); + assertThat("getScreenName should return the last screen name set", + mTransportControl.getScreenName(), equalTo("Name2")); + // Now there should have been two calls + verify(mTransportControl, times(2)).onScreenNameChanged(); + } + + /** + * Test method for {@link TransportControl#setError} . + */ + @Test + @Feature("MediaRemote") + public void testSetError() { + mTransportControl.setError("Error1"); + assertThat("getError should return the last error set", mTransportControl.getError(), + equalTo("Error1")); + // Check that onErrorChanged is called precisely once + verify(mTransportControl).onErrorChanged(); + // Try setting the same error again + mTransportControl.setError("Error1"); + // There should still have only been one call to onErrorChanged + verify(mTransportControl).onErrorChanged(); + // And try a different error message + mTransportControl.setError("Error2"); + assertThat("getError should return the last screen name set", mTransportControl.getError(), + equalTo("Error2")); + // Now there should have been two calls + verify(mTransportControl, times(2)).onErrorChanged(); + // Now try with an empty string + mTransportControl.setError(""); + assertThat("empty error string should set error to null", mTransportControl.getError(), + nullValue()); + verify(mTransportControl, times(3)).onErrorChanged(); + // Try setting the empty string a second time, should not call onErrorChanged + mTransportControl.setError(""); + verify(mTransportControl, times(3)).onErrorChanged(); + // Also try null, which should be equivalent to an empty string + mTransportControl.setError(null); + verify(mTransportControl, times(3)).onErrorChanged(); + } + + /** + * Test method for {@link TransportControl#setVideoInfo} . + */ + @Test + @Feature("MediaRemote") + public void testSetVideoInfo() { + RemoteVideoInfo videoInfo1 = new RemoteVideoInfo("Title1", 10, + RemoteVideoInfo.PlayerState.STOPPED, 5, "Error1"); + RemoteVideoInfo videoInfo2 = new RemoteVideoInfo("Title1", 10, + RemoteVideoInfo.PlayerState.STOPPED, 5, "Error1"); + RemoteVideoInfo videoInfo3 = new RemoteVideoInfo("Title3", 10, + RemoteVideoInfo.PlayerState.STOPPED, 5, "Error1"); + mTransportControl.setVideoInfo(videoInfo1); + assertThat("getVideoInfo should return the videoInfo that was set", + mTransportControl.getVideoInfo(), equalTo(videoInfo1)); + // Check that onVideoInfoChanged is called precisely once + verify(mTransportControl).onVideoInfoChanged(); + // Try setting the same video info again + mTransportControl.setVideoInfo(videoInfo1); + // There should still have only been one call to onVideoInfoChanged + verify(mTransportControl).onVideoInfoChanged(); + // Set the video info to a copy of the original video info + mTransportControl.setVideoInfo(videoInfo2); + // There should still have only been one call to onVideoInfoChanged + verify(mTransportControl).onVideoInfoChanged(); + // And try a different video info + mTransportControl.setVideoInfo(videoInfo3); + assertThat("getVideoInfo should return the last videoInfo set", + mTransportControl.getVideoInfo(), equalTo(videoInfo3)); + // Now there should have been two calls + verify(mTransportControl, times(2)).onVideoInfoChanged(); + + } + + /** + * Test method for {@link TransportControl#setPosterBitmap} . + */ + @Test + @Feature("MediaRemote") + public void testSetPosterBitmap() { + Bitmap.Config conf = Bitmap.Config.ARGB_8888; + int c[] = new int[] {Color.RED}; + Bitmap bmp1 = Bitmap.createBitmap(c, 1, 1, conf); + mTransportControl.setPosterBitmap(bmp1); + assertThat("getPosterBitmap gets the last set poster", mTransportControl.getPosterBitmap(), + equalTo(bmp1)); + // onPosterBitmapChanged should have been called precisely once + verify(mTransportControl).onPosterBitmapChanged(); + // Try setting the same poster again + mTransportControl.setPosterBitmap(bmp1); + // there should still have been precisely one call to onPosterBitmapChanged + verify(mTransportControl).onPosterBitmapChanged(); + // TODO(aberent) Cannot test changing the bitmap, because of a bug in Robolectric bitmap + // comparison (https://github.com/robolectric/robolectric/issues/1684). + } + + /** + * Test method for {@link TransportControl#addListener} Also tests getListener + */ + @Test + @Feature("MediaRemote") + public void testAddListener() { + assertThat("The listener set is empty", mTransportControl.getListeners().size(), + equalTo(0)); + TransportControl.Listener listener1 = Mockito.mock(TransportControl.Listener.class); + mTransportControl.addListener(listener1); + assertThat("The listener set contains one item", mTransportControl.getListeners().size(), + equalTo(1)); + // TODO(aberent): Change to CoreMatchers.hasItems when Hamcrest has been upgraded to a more + // modern version + assertThat("An added listener is returned", + mTransportControl.getListeners().contains(listener1), equalTo(true)); + // Add a second listener + TransportControl.Listener listener2 = Mockito.mock(TransportControl.Listener.class); + mTransportControl.addListener(listener2); + assertThat("The listener set contains two items", mTransportControl.getListeners().size(), + equalTo(2)); + // TODO(aberent): Change to CoreMatchers.hasItems when Hamcrest has been upgraded to a more + // modern version + assertThat("Adding a listener does not remove the old listener", + mTransportControl.getListeners().contains(listener1), equalTo(true)); + assertThat("The second added listener is returned", + mTransportControl.getListeners().contains(listener2), equalTo(true)); + } + + /** + * Test method for {@link TransportControl#removeListener} . + */ + @Test + @Feature("MediaRemote") + public void testRemoveListener() { + TransportControl.Listener listener1 = Mockito.mock(TransportControl.Listener.class); + mTransportControl.addListener(listener1); + TransportControl.Listener listener2 = Mockito.mock(TransportControl.Listener.class); + mTransportControl.addListener(listener2); + mTransportControl.removeListener(listener1); + assertThat("The listener set contains one item", mTransportControl.getListeners().size(), + equalTo(1)); + assertThat("The removed listner is no longer in the list", + mTransportControl.getListeners().contains(listener1), equalTo(false)); + } + +}
diff --git a/chrome/android/shell/java/src/org/chromium/chrome/shell/ChromeShellTabModelSelector.java b/chrome/android/shell/java/src/org/chromium/chrome/shell/ChromeShellTabModelSelector.java index 2042d70..03067ad 100644 --- a/chrome/android/shell/java/src/org/chromium/chrome/shell/ChromeShellTabModelSelector.java +++ b/chrome/android/shell/java/src/org/chromium/chrome/shell/ChromeShellTabModelSelector.java
@@ -6,7 +6,6 @@ import android.content.Context; -import org.chromium.chrome.browser.ChromeContentViewClient; import org.chromium.chrome.browser.Tab; import org.chromium.chrome.browser.tabmodel.EmptyTabModel; import org.chromium.chrome.browser.tabmodel.TabModel; @@ -86,7 +85,7 @@ public Tab openNewTab(LoadUrlParams loadUrlParams, TabLaunchType type, Tab parent, boolean incognito) { assert !incognito; - ContentViewClient client = new ChromeContentViewClient() { + ContentViewClient client = new ContentViewClient() { @Override public ContentVideoViewClient getContentVideoViewClient() { return mContentVideoViewClient;
diff --git a/chrome/app/chromeos_strings.grdp b/chrome/app/chromeos_strings.grdp index 2e3bebd..3b458688 100644 --- a/chrome/app/chromeos_strings.grdp +++ b/chrome/app/chromeos_strings.grdp
@@ -1962,7 +1962,7 @@ Connected </message> <message name="IDS_CHROMEOS_NETWORK_STATE_CONNECTING" desc="Displayed state for a network when connecting"> - Not Connected + Connecting </message> <message name="IDS_OPTIONS_SETTINGS_NETWORK_DISABLED" desc="Message displayed when a type of network connection is disabled">
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd index 2fbc79a..0c3901e 100644 --- a/chrome/app/generated_resources.grd +++ b/chrome/app/generated_resources.grd
@@ -6706,6 +6706,12 @@ <message name="IDS_FLAGS_ENABLE_PERMISSIONS_BUBBLES_DESCRIPTION" desc="Description for the flag to enable showing permissions requests in bubbles."> Show content permission requests (e.g. notifications, quota, camera use, microphone use) in bubbles instead of info bars. </message> + <message name="IDS_FLAGS_ENABLE_SITE_ENGAGEMENT_SERVICE_NAME" desc="Title for the flag to enable the site engagement service."> + Enable Site Engagement Service + </message> + <message name="IDS_FLAGS_ENABLE_SITE_ENGAGEMENT_SERVICE_DESCRIPTION" desc="Description for the flag to enable the site engagement service."> + Enables the Site Engagement Service, which records interaction with sites and allocates resources accordingly. + </message> <message name="IDS_FLAGS_OUT_OF_PROCESS_PDF_NAME" desc="Title for the flag to enable out of process PDF."> Enable out of process PDF. </message> @@ -6787,6 +6793,12 @@ <message name="IDS_FLAGS_ENABLE_TAB_AUDIO_MUTING_DESCRIPTION" desc="Description of the flag that enables the tab audio muting UI experiment in chrome://extensions."> When enabled, the audio indicators in the tab strip double as tab audio mute controls. This also adds commands in the tab context menu for quickly muting multiple selected tabs. </message> + <message name="IDS_FLAGS_ENABLE_EASY_UNLOCK_BLUETOOTH_LOW_ENERGY_DISCOVERY_NAME" desc="Title for the flag to enable Smart Lock to discover phones over Bluetooth Low Energy in order to unlock the Chromebook."> + Enable Smart Lock Bluetooth Low Energy Discovery. + </message> + <message name="IDS_FLAGS_ENABLE_EASY_UNLOCK_BLUETOOTH_LOW_ENERGY_DISCOVERY_DESCRIPTION" desc="Description for the flag to enable Smart Lock to discover phones over Bluetooth Low Energy in order to unlock the Chromebook."> + Enables a Smart Lock setting that allows Chromebook to discover phones over Bleutooth Low Energy in order to unlock the Chromebook when the phone is in its proximity. + </message> <message name="IDS_FLAGS_ENABLE_EASY_UNLOCK_PROXIMITY_DETECTION_NAME" desc="Title for the flag to enable Smart Lock to require close proximity between the phone and the Chromebook in order to unlock the Chromebook."> Enable Smart Lock proximity detection. </message> @@ -9199,9 +9211,9 @@ from accessing the network. </message> <message name="IDS_ERRORPAGES_SUMMARY_ICANN_NAME_COLLISION" desc="Summary in the error page when a dns look up fails due to having contained 127.0.53.53."> - This site is using a + This site is using a <ph name="BEGIN_LINK"><a href="https://support.google.com/chrome/?p=top_level_domain&hl=[GRITLANGCODE]"></ph>new generic top-level domain<ph name="END_LINK"></a><ex></a></ex></ph> - (gTLD). If you have used + (gTLD). If you have used <ph name="HOST_NAME"><strong jscontent="hostName"></strong><ex>www.whatever.com</ex></ph> to access an internal site in the past, contact your network administrator. @@ -15105,14 +15117,30 @@ <!-- Reader mode experiment flags --> <if expr="is_android"> + <message name="IDS_FLAGS_READER_MODE_HEURISTICS_NAME" desc="A name of an about:flags experiment for controlling when to show the reader mode button"> + Reader Mode triggering + </message> + <message name="IDS_FLAGS_READER_MODE_HEURISTICS_DESCRIPTION" desc="Describes about:flags experiment options for controlling when to show the reader mode button"> + Determines what pages the Reader Mode button is shown on. + </message> + <message name="IDS_FLAGS_READER_MODE_HEURISTICS_MARKUP" desc="A choice in dropdown dialog on about:flags page to show the reader mode button with article structured markup"> + Show the Reader Mode button on pages with article structured markup + </message> + <message name="IDS_FLAGS_READER_MODE_HEURISTICS_ADABOOST" desc="A choice in dropdown dialog on about:flags page to show the reader mode button on pages that appear to be long form content"> + Show the Reader Mode button on pages that appear to be long form content + </message> + <message name="IDS_FLAGS_READER_MODE_HEURISTICS_ALWAYS_OFF" desc="A choice in dropdown dialog on about:flags page to never show the reader mode button"> + Never show Reader Mode button + </message> + <message name="IDS_FLAGS_READER_MODE_HEURISTICS_ALWAYS_ON" desc="A choice in dropdown dialog on about:flags page to show the reader mode button on all pages"> + Show Reader Mode button on all pages + </message> <message name="IDS_FLAGS_READER_MODE_EXPERIMENT_NAME" desc="An about:flags experiment for reading mode UI"> Enable Reader Mode Toolbar Icon </message> <message name="IDS_FLAGS_READER_MODE_EXPERIMENT_DESCRIPTION" desc="Describes about:flags experiment options for reading mode UI"> Adds a button to the toolbar for viewing a more readable version of the current page. </message> - </if> - <if expr="is_android"> <message name="IDS_FLAGS_READER_MODE_BUTTON_ANIMATION" desc="Title for flag enabling the reader mode button animation"> Enable Reader Mode Button Animation </message>
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn index adb4c14..c6925c3 100644 --- a/chrome/browser/BUILD.gn +++ b/chrome/browser/BUILD.gn
@@ -185,6 +185,9 @@ sources += rebase_path(gypi_values.chrome_browser_content_settings_sources, ".", "//chrome") + sources += rebase_path(gypi_values.chrome_browser_engagement_sources, + ".", + "//chrome") sources += rebase_path(gypi_values.chrome_browser_favicon_sources, ".", "//chrome") sources += @@ -332,6 +335,7 @@ "//chrome/browser/sync_file_system/drive_backend:sync_file_system_drive_proto", "//chrome/common/extensions/api", "//chrome/common/extensions/api:api_registration", + "//components/proximity_auth/ble", "//components/proximity_auth/cryptauth", "//extensions/components/javascript_dialog_extensions_client", "//media/cast:net",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc index 767afd80..f8e446e 100644 --- a/chrome/browser/about_flags.cc +++ b/chrome/browser/about_flags.cc
@@ -26,6 +26,7 @@ #include "chrome/grit/google_chrome_strings.h" #include "components/autofill/core/common/autofill_switches.h" #include "components/cloud_devices/common/cloud_devices_switches.h" +#include "components/dom_distiller/core/dom_distiller_switches.h" #include "components/metrics/metrics_hashes.h" #include "components/nacl/common/nacl_switches.h" #include "components/omnibox/omnibox_switches.h" @@ -290,6 +291,22 @@ { IDS_GENERIC_EXPERIMENT_CHOICE_DISABLED, switches::kDisableZeroSuggest, ""} }; + +const Experiment::Choice kReaderModeHeuristicsChoices[] = { + { IDS_GENERIC_EXPERIMENT_CHOICE_DEFAULT, "", ""}, + { IDS_FLAGS_READER_MODE_HEURISTICS_MARKUP, + switches::kReaderModeHeuristics, + switches::reader_mode_heuristics::kOGArticle }, + { IDS_FLAGS_READER_MODE_HEURISTICS_ADABOOST, + switches::kReaderModeHeuristics, + switches::reader_mode_heuristics::kAdaBoost }, + { IDS_FLAGS_READER_MODE_HEURISTICS_ALWAYS_ON, + switches::kReaderModeHeuristics, + switches::reader_mode_heuristics::kAlwaysTrue }, + { IDS_FLAGS_READER_MODE_HEURISTICS_ALWAYS_OFF, + switches::kReaderModeHeuristics, + switches::reader_mode_heuristics::kNone }, +}; #endif const Experiment::Choice kNumRasterThreadsChoices[] = { @@ -938,6 +955,14 @@ kOsCrOS, SINGLE_VALUE_TYPE(proximity_auth::switches::kEnableProximityDetection) }, + { + "enable-easy-unlock-bluetooth-low-energy-detection", + IDS_FLAGS_ENABLE_EASY_UNLOCK_BLUETOOTH_LOW_ENERGY_DISCOVERY_NAME, + IDS_FLAGS_ENABLE_EASY_UNLOCK_BLUETOOTH_LOW_ENERGY_DISCOVERY_DESCRIPTION, + kOsCrOS, + SINGLE_VALUE_TYPE( + proximity_auth::switches::kEnableBluetoothLowEnergyDiscovery) + }, #endif #if defined(USE_ASH) { @@ -1733,6 +1758,13 @@ MULTI_VALUE_TYPE(kZeroSuggestExperimentsChoices) }, { + "reader-mode-heuristics", + IDS_FLAGS_READER_MODE_HEURISTICS_NAME, + IDS_FLAGS_READER_MODE_HEURISTICS_DESCRIPTION, + kOsAndroid, + MULTI_VALUE_TYPE(kReaderModeHeuristicsChoices) + }, + { "enable-reader-mode-toolbar-icon", IDS_FLAGS_READER_MODE_EXPERIMENT_NAME, IDS_FLAGS_READER_MODE_EXPERIMENT_DESCRIPTION, @@ -1772,6 +1804,14 @@ switches::kDisablePermissionsBubbles) }, { + "enable-site-engagement-service", + IDS_FLAGS_ENABLE_SITE_ENGAGEMENT_SERVICE_NAME, + IDS_FLAGS_ENABLE_SITE_ENGAGEMENT_SERVICE_DESCRIPTION, + kOsAll, + ENABLE_DISABLE_VALUE_TYPE(switches::kEnableSiteEngagementService, + switches::kDisableSiteEngagementService) + }, + { "enable-session-crashed-bubble", IDS_FLAGS_ENABLE_SESSION_CRASHED_BUBBLE_NAME, IDS_FLAGS_ENABLE_SESSION_CRASHED_BUBBLE_DESCRIPTION,
diff --git a/chrome/browser/android/banners/app_banner_manager_android.cc b/chrome/browser/android/banners/app_banner_manager_android.cc index ff2c8b3..829b0b0 100644 --- a/chrome/browser/android/banners/app_banner_manager_android.cc +++ b/chrome/browser/android/banners/app_banner_manager_android.cc
@@ -21,7 +21,7 @@ using base::android::ConvertUTF16ToJavaString; namespace { -const char kBannerTag[] = "google-play-id"; +const char kPlayPlatform[] = "play"; } // anonymous namespace namespace banners { @@ -48,26 +48,26 @@ AppBannerManager::ReplaceWebContents(web_contents); } -bool AppBannerManagerAndroid::OnMessageReceived( - const IPC::Message& message) { - bool handled = true; - IPC_BEGIN_MESSAGE_MAP(AppBannerManagerAndroid, message) - IPC_MESSAGE_HANDLER(ChromeViewHostMsg_DidRetrieveMetaTagContent, - OnDidRetrieveMetaTagContent) - IPC_MESSAGE_UNHANDLED(handled = false) - IPC_END_MESSAGE_MAP() - return handled; -} - -bool AppBannerManagerAndroid::OnInvalidManifest(AppBannerDataFetcher* fetcher) { - DCHECK(data_fetcher() == fetcher); - if (web_contents()->IsBeingDestroyed()) { +bool AppBannerManagerAndroid::HandleNonWebApp(const std::string& platform, + const GURL& url, + const std::string& id) { + if (platform != kPlayPlatform || id.empty()) return false; - } - Send(new ChromeViewMsg_RetrieveMetaTagContent(routing_id(), - fetcher->validated_url(), - kBannerTag)); + banners::TrackDisplayEvent(DISPLAY_EVENT_BANNER_REQUESTED); + + // Send the info to the Java side to get info about the app. + JNIEnv* env = base::android::AttachCurrentThread(); + ScopedJavaLocalRef<jobject> jobj = weak_java_banner_view_manager_.get(env); + if (jobj.is_null()) + return false; + + ScopedJavaLocalRef<jstring> jurl( + ConvertUTF8ToJavaString(env, data_fetcher()->validated_url().spec())); + ScopedJavaLocalRef<jstring> jpackage( + ConvertUTF8ToJavaString(env, id)); + Java_AppBannerManager_fetchAppDetails( + env, jobj.obj(), jurl.obj(), jpackage.obj(), ideal_icon_size()); return true; } @@ -78,36 +78,6 @@ ideal_icon_size); } -void AppBannerManagerAndroid::OnDidRetrieveMetaTagContent( - bool success, - const std::string& tag_name, - const std::string& tag_content, - const GURL& expected_url) { - DCHECK(web_contents()); - if (!success - || tag_name != kBannerTag - || !data_fetcher() - || data_fetcher()->validated_url() != expected_url - || tag_content.size() >= chrome::kMaxMetaTagAttributeLength) { - return; - } - - banners::TrackDisplayEvent(DISPLAY_EVENT_BANNER_REQUESTED); - - // Send the info to the Java side to get info about the app. - JNIEnv* env = base::android::AttachCurrentThread(); - ScopedJavaLocalRef<jobject> jobj = weak_java_banner_view_manager_.get(env); - if (jobj.is_null()) - return; - - ScopedJavaLocalRef<jstring> jurl( - ConvertUTF8ToJavaString(env, expected_url.spec())); - ScopedJavaLocalRef<jstring> jpackage( - ConvertUTF8ToJavaString(env, tag_content)); - Java_AppBannerManager_fetchAppDetails( - env, jobj.obj(), jurl.obj(), jpackage.obj(), ideal_icon_size()); -} - bool AppBannerManagerAndroid::OnAppDetailsRetrieved(JNIEnv* env, jobject obj, jobject japp_data,
diff --git a/chrome/browser/android/banners/app_banner_manager_android.h b/chrome/browser/android/banners/app_banner_manager_android.h index f63b556..f93a1b7 100644 --- a/chrome/browser/android/banners/app_banner_manager_android.h +++ b/chrome/browser/android/banners/app_banner_manager_android.h
@@ -47,26 +47,16 @@ jstring japp_package, jstring jicon_url); - // WebContentsObserver overrides. - bool OnMessageReceived(const IPC::Message& message) override; - - // AppBannerDataFetcher::Delegate overrides. - bool OnInvalidManifest(AppBannerDataFetcher* fetcher) override; - protected: AppBannerDataFetcher* CreateAppBannerDataFetcher( base::WeakPtr<AppBannerDataFetcher::Delegate> weak_delegate, const int ideal_icon_size) override; private: - // Called when the renderer has returned information about the meta tag. - // If there is some metadata for the play store tag, this kicks off the - // process of showing a banner for the package designated by |tag_content| on - // the page at the |expected_url|. - void OnDidRetrieveMetaTagContent(bool success, - const std::string& tag_name, - const std::string& tag_content, - const GURL& expected_url); + // AppBannerDataFetcher::Delegate overrides. + bool HandleNonWebApp(const std::string& platform, + const GURL& url, + const std::string& id) override; // AppBannerManager on the Java side. JavaObjectWeakGlobalRef weak_java_banner_view_manager_;
diff --git a/chrome/browser/android/chromium_application.cc b/chrome/browser/android/chromium_application.cc index 7b5a5a6..8b98d43 100644 --- a/chrome/browser/android/chromium_application.cc +++ b/chrome/browser/android/chromium_application.cc
@@ -29,7 +29,7 @@ void FlushCookiesOnIOThread( scoped_refptr<net::URLRequestContextGetter> getter) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); getter->GetURLRequestContext() ->cookie_store() ->GetCookieMonster() @@ -50,7 +50,7 @@ void RemoveSessionCookiesOnIOThread( scoped_refptr<net::URLRequestContextGetter> getter) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); getter->GetURLRequestContext()->cookie_store()->DeleteSessionCookiesAsync( net::CookieStore::DeleteCallback()); } @@ -64,7 +64,7 @@ void ChangeAppStatusOnIOThread(SafeBrowsingService* sb_service, jboolean foreground) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); SafeBrowsingProtocolManager* proto_manager = sb_service->protocol_manager(); if (proto_manager) proto_manager->SetAppInForeground(foreground);
diff --git a/chrome/browser/android/compositor/tab_content_manager.cc b/chrome/browser/android/compositor/tab_content_manager.cc index 4a8691b0..0340aac 100644 --- a/chrome/browser/android/compositor/tab_content_manager.cc +++ b/chrome/browser/android/compositor/tab_content_manager.cc
@@ -142,14 +142,14 @@ disk_cache_path_str, (size_t)default_cache_size, (size_t)approximation_cache_size, (size_t)compression_queue_max_size, (size_t)write_queue_max_size, use_approximation_thumbnail)); - thumbnail_cache_->AddThumbnailStoreObserver(this); + thumbnail_cache_->AddThumbnailCacheObserver(this); } TabContentManager::~TabContentManager() { } void TabContentManager::Destroy(JNIEnv* env, jobject obj) { - thumbnail_cache_->RemoveThumbnailStoreObserver(this); + thumbnail_cache_->RemoveThumbnailCacheObserver(this); delete this; }
diff --git a/chrome/browser/android/compositor/tab_content_manager.h b/chrome/browser/android/compositor/tab_content_manager.h index f09366b..10e4910 100644 --- a/chrome/browser/android/compositor/tab_content_manager.h +++ b/chrome/browser/android/compositor/tab_content_manager.h
@@ -13,7 +13,7 @@ #include "base/containers/scoped_ptr_hash_map.h" #include "base/memory/weak_ptr.h" #include "cc/layers/ui_resource_layer.h" -#include "chrome/browser/android/thumbnail/thumbnail_store.h" +#include "chrome/browser/android/thumbnail/thumbnail_cache.h" using base::android::ScopedJavaLocalRef; @@ -32,7 +32,7 @@ class ThumbnailLayer; // A native component of the Java TabContentManager class. -class TabContentManager : public ThumbnailStoreObserver { +class TabContentManager : public ThumbnailCacheObserver { public: static TabContentManager* FromJavaObject(jobject jobj); @@ -92,7 +92,7 @@ jint min_forbidden_id); void GetDecompressedThumbnail(JNIEnv* env, jobject obj, jint tab_id); - // ThumbnailStoreObserver implementation; + // ThumbnailCacheObserver implementation; void OnFinishedThumbnailRead(TabId tab_id) override; private: @@ -101,11 +101,6 @@ typedef base::hash_map<int, scoped_refptr<ThumbnailLayer>> ThumbnailLayerMap; typedef base::ScopedPtrHashMap<int, TabReadbackRequest> TabReadbackRequestMap; - // TODO(): The upstream ThumbnailCache class was temporarily renamed to - // ThumbnailStore to avoid conflict with downstream. Please rename the - // upstream to ThumbnailCache once the downstream is in a good state. - typedef ThumbnailStore ThumbnailCache; - void PutThumbnailIntoCache(int tab_id, float thumbnail_scale, const SkBitmap& bitmap);
diff --git a/chrome/browser/android/cookies/cookies_fetcher.cc b/chrome/browser/android/cookies/cookies_fetcher.cc index 25f55e7f..8197f37 100644 --- a/chrome/browser/android/cookies/cookies_fetcher.cc +++ b/chrome/browser/android/cookies/cookies_fetcher.cc
@@ -49,7 +49,7 @@ void CookiesFetcher::PersistCookiesInternal( net::URLRequestContextGetter* getter) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); net::CookieStore* store = getter->GetURLRequestContext()->cookie_store(); @@ -142,7 +142,7 @@ void CookiesFetcher::RestoreToCookieJarInternal( net::URLRequestContextGetter* getter, const net::CanonicalCookie& cookie) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); net::CookieStore* store = getter->GetURLRequestContext()->cookie_store();
diff --git a/chrome/browser/android/download/chrome_download_manager_overwrite_infobar_delegate.cc b/chrome/browser/android/download/chrome_download_manager_overwrite_infobar_delegate.cc index f4002b4..d4c0cf8c 100644 --- a/chrome/browser/android/download/chrome_download_manager_overwrite_infobar_delegate.cc +++ b/chrome/browser/android/download/chrome_download_manager_overwrite_infobar_delegate.cc
@@ -73,7 +73,7 @@ void ChromeDownloadManagerOverwriteInfoBarDelegate::CreateNewFileInternal( const base::FilePath& suggested_download_path, const DownloadTargetDeterminerDelegate::FileSelectedCallback& callback) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE)); + DCHECK_CURRENTLY_ON(content::BrowserThread::FILE); int uniquifier = base::GetUniquePathNumber(suggested_download_path, base::FilePath::StringType()); base::FilePath new_path = suggested_download_path;
diff --git a/chrome/browser/android/logo_service.cc b/chrome/browser/android/logo_service.cc index c71cefd..fc113ff7 100644 --- a/chrome/browser/android/logo_service.cc +++ b/chrome/browser/android/logo_service.cc
@@ -40,8 +40,7 @@ public: LogoDecoderDelegate( const base::Callback<void(const SkBitmap&)>& image_decoded_callback) - : ImageRequest(base::MessageLoopProxy::current()), - image_decoded_callback_(image_decoded_callback), + : image_decoded_callback_(image_decoded_callback), weak_ptr_factory_(this) { // If the ImageDecoder crashes or otherwise never completes, call // OnImageDecodeTimedOut() eventually to ensure that image_decoded_callback_
diff --git a/chrome/browser/android/provider/blocking_ui_thread_async_request.cc b/chrome/browser/android/provider/blocking_ui_thread_async_request.cc index 7267e583..4eb9811e 100644 --- a/chrome/browser/android/provider/blocking_ui_thread_async_request.cc +++ b/chrome/browser/android/provider/blocking_ui_thread_async_request.cc
@@ -12,6 +12,6 @@ // Currently all our use cases receive their request response in the UI // thread (the same thread that made the request). However this is not // a design constraint and can be changed if ever needed. - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); request_completed_.Signal(); }
diff --git a/chrome/browser/android/provider/run_on_ui_thread_blocking.h b/chrome/browser/android/provider/run_on_ui_thread_blocking.h index 04ea537..b43410e1 100644 --- a/chrome/browser/android/provider/run_on_ui_thread_blocking.h +++ b/chrome/browser/android/provider/run_on_ui_thread_blocking.h
@@ -30,7 +30,7 @@ template <typename Signature> static void RunOnUIThread(base::Callback<Signature> runnable, base::WaitableEvent* finished) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); runnable.Run(); finished->Signal(); }
diff --git a/chrome/browser/android/seccomp_support_detector.cc b/chrome/browser/android/seccomp_support_detector.cc index 632cde2..d53911b 100644 --- a/chrome/browser/android/seccomp_support_detector.cc +++ b/chrome/browser/android/seccomp_support_detector.cc
@@ -36,7 +36,7 @@ base::Bind(&SeccompSupportDetector::DetectKernelVersion, detector)); } -SeccompSupportDetector::SeccompSupportDetector() : prctl_detected_(false) { +SeccompSupportDetector::SeccompSupportDetector() { } SeccompSupportDetector::~SeccompSupportDetector() { @@ -64,8 +64,6 @@ #else BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, base::Bind(&SeccompSupportDetector::OnDetectPrctl, this, false)); - BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, - base::Bind(&SeccompSupportDetector::OnDetectSyscall, this, false)); #endif } @@ -82,17 +80,9 @@ void SeccompSupportDetector::OnProcessCrashed(int exit_code) { DCHECK_CURRENTLY_ON(BrowserThread::IO); - // The process crashed. Since prctl detection happens first, report which - // probe failed. - if (prctl_detected_) { - UMA_HISTOGRAM_ENUMERATION("Android.SeccompStatus.Syscall", - DETECTION_FAILED, - LAST_STATUS); - } else { - UMA_HISTOGRAM_ENUMERATION("Android.SeccompStatus.Prctl", - DETECTION_FAILED, - LAST_STATUS); - } + UMA_HISTOGRAM_ENUMERATION("Android.SeccompStatus.Prctl", + DETECTION_FAILED, + LAST_STATUS); } bool SeccompSupportDetector::OnMessageReceived(const IPC::Message& message) { @@ -100,8 +90,6 @@ IPC_BEGIN_MESSAGE_MAP(SeccompSupportDetector, message) IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_DetectSeccompSupport_ResultPrctl, OnDetectPrctl) - IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_DetectSeccompSupport_ResultSyscall, - OnDetectSyscall) IPC_MESSAGE_UNHANDLED(handled = false) IPC_END_MESSAGE_MAP() return handled; @@ -109,22 +97,10 @@ void SeccompSupportDetector::OnDetectPrctl(bool prctl_supported) { DCHECK_CURRENTLY_ON(BrowserThread::IO); - DCHECK(!prctl_detected_); - - prctl_detected_ = true; UMA_HISTOGRAM_ENUMERATION("Android.SeccompStatus.Prctl", prctl_supported ? SUPPORTED : NOT_SUPPORTED, LAST_STATUS); -} - -void SeccompSupportDetector::OnDetectSyscall(bool syscall_supported) { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - DCHECK(prctl_detected_); - - UMA_HISTOGRAM_ENUMERATION("Android.SeccompStatus.Syscall", - syscall_supported ? SUPPORTED : NOT_SUPPORTED, - LAST_STATUS); // The utility process will shutdown after this, and this object will // be deleted when the UtilityProcessHost releases its reference.
diff --git a/chrome/browser/android/seccomp_support_detector.h b/chrome/browser/android/seccomp_support_detector.h index f038d51..58d66bd 100644 --- a/chrome/browser/android/seccomp_support_detector.h +++ b/chrome/browser/android/seccomp_support_detector.h
@@ -33,12 +33,7 @@ void OnProcessCrashed(int exit_code) override; bool OnMessageReceived(const IPC::Message& message) override; - // OnDetectPrctl is always received before OnDetectSyscall. void OnDetectPrctl(bool prctl_supported); - void OnDetectSyscall(bool syscall_supported); - - // Whether OnDetectPrctl was received. - bool prctl_detected_; DISALLOW_COPY_AND_ASSIGN(SeccompSupportDetector); };
diff --git a/chrome/browser/android/thumbnail/thumbnail_store.cc b/chrome/browser/android/thumbnail/thumbnail_cache.cc similarity index 90% rename from chrome/browser/android/thumbnail/thumbnail_store.cc rename to chrome/browser/android/thumbnail/thumbnail_cache.cc index 4fe4472..130385d 100644 --- a/chrome/browser/android/thumbnail/thumbnail_store.cc +++ b/chrome/browser/android/thumbnail/thumbnail_cache.cc
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "chrome/browser/android/thumbnail/thumbnail_store.h" +#include "chrome/browser/android/thumbnail/thumbnail_cache.h" #include <algorithm> #include <cmath> @@ -111,7 +111,7 @@ } // anonymous namespace -ThumbnailStore::ThumbnailStore(const std::string& disk_cache_path_str, +ThumbnailCache::ThumbnailCache(const std::string& disk_cache_path_str, size_t default_cache_size, size_t approximation_cache_size, size_t compression_queue_max_size, @@ -128,14 +128,14 @@ approximation_cache_(approximation_cache_size), ui_resource_provider_(NULL), weak_factory_(this) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); } -ThumbnailStore::~ThumbnailStore() { +ThumbnailCache::~ThumbnailCache() { SetUIResourceProvider(NULL); } -void ThumbnailStore::SetUIResourceProvider( +void ThumbnailCache::SetUIResourceProvider( ui::UIResourceProvider* ui_resource_provider) { if (ui_resource_provider_ == ui_resource_provider) return; @@ -146,19 +146,19 @@ ui_resource_provider_ = ui_resource_provider; } -void ThumbnailStore::AddThumbnailStoreObserver( - ThumbnailStoreObserver* observer) { +void ThumbnailCache::AddThumbnailCacheObserver( + ThumbnailCacheObserver* observer) { if (!observers_.HasObserver(observer)) observers_.AddObserver(observer); } -void ThumbnailStore::RemoveThumbnailStoreObserver( - ThumbnailStoreObserver* observer) { +void ThumbnailCache::RemoveThumbnailCacheObserver( + ThumbnailCacheObserver* observer) { if (observers_.HasObserver(observer)) observers_.RemoveObserver(observer); } -void ThumbnailStore::Put(TabId tab_id, +void ThumbnailCache::Put(TabId tab_id, const SkBitmap& bitmap, float thumbnail_scale) { if (!ui_resource_provider_ || bitmap.empty() || thumbnail_scale <= 0) @@ -186,7 +186,7 @@ CompressThumbnailIfNecessary(tab_id, time_stamp, bitmap, thumbnail_scale); } -void ThumbnailStore::Remove(TabId tab_id) { +void ThumbnailCache::Remove(TabId tab_id) { cache_.Remove(tab_id); approximation_cache_.Remove(tab_id); thumbnail_meta_data_.erase(tab_id); @@ -194,7 +194,7 @@ RemoveFromReadQueue(tab_id); } -Thumbnail* ThumbnailStore::Get(TabId tab_id, +Thumbnail* ThumbnailCache::Get(TabId tab_id, bool force_disk_read, bool allow_approximation) { Thumbnail* thumbnail = cache_.Get(tab_id); @@ -223,16 +223,16 @@ return NULL; } -void ThumbnailStore::RemoveFromDiskAtAndAboveId(TabId min_id) { +void ThumbnailCache::RemoveFromDiskAtAndAboveId(TabId min_id) { base::Closure remove_task = - base::Bind(&ThumbnailStore::RemoveFromDiskAtAndAboveIdTask, + base::Bind(&ThumbnailCache::RemoveFromDiskAtAndAboveIdTask, disk_cache_path_, min_id); content::BrowserThread::PostTask( content::BrowserThread::FILE, FROM_HERE, remove_task); } -void ThumbnailStore::InvalidateThumbnailIfChanged(TabId tab_id, +void ThumbnailCache::InvalidateThumbnailIfChanged(TabId tab_id, const GURL& url) { ThumbnailMetaDataMap::iterator meta_data_iter = thumbnail_meta_data_.find(tab_id); @@ -243,7 +243,7 @@ } } -bool ThumbnailStore::CheckAndUpdateThumbnailMetaData(TabId tab_id, +bool ThumbnailCache::CheckAndUpdateThumbnailMetaData(TabId tab_id, const GURL& url) { base::Time current_time = base::Time::Now(); ThumbnailMetaDataMap::iterator meta_data_iter = @@ -259,7 +259,7 @@ return true; } -void ThumbnailStore::UpdateVisibleIds(const TabIdList& priority) { +void ThumbnailCache::UpdateVisibleIds(const TabIdList& priority) { if (priority.empty()) { visible_ids_.clear(); return; @@ -305,7 +305,7 @@ ReadNextThumbnail(); } -void ThumbnailStore::DecompressThumbnailFromFile( +void ThumbnailCache::DecompressThumbnailFromFile( TabId tab_id, const base::Callback<void(bool, SkBitmap)>& post_decompress_callback) { @@ -313,28 +313,28 @@ base::Callback<void(skia::RefPtr<SkPixelRef>, float, const gfx::Size&)> decompress_task = base::Bind( - &ThumbnailStore::DecompressionTask, post_decompress_callback); + &ThumbnailCache::DecompressionTask, post_decompress_callback); content::BrowserThread::PostTask( content::BrowserThread::FILE, FROM_HERE, - base::Bind(&ThumbnailStore::ReadTask, true, file_path, decompress_task)); + base::Bind(&ThumbnailCache::ReadTask, true, file_path, decompress_task)); } -void ThumbnailStore::RemoveFromDisk(TabId tab_id) { +void ThumbnailCache::RemoveFromDisk(TabId tab_id) { base::FilePath file_path = GetFilePath(tab_id); base::Closure task = - base::Bind(&ThumbnailStore::RemoveFromDiskTask, file_path); + base::Bind(&ThumbnailCache::RemoveFromDiskTask, file_path); content::BrowserThread::PostTask( content::BrowserThread::FILE, FROM_HERE, task); } -void ThumbnailStore::RemoveFromDiskTask(const base::FilePath& file_path) { +void ThumbnailCache::RemoveFromDiskTask(const base::FilePath& file_path) { if (base::PathExists(file_path)) base::DeleteFile(file_path, false); } -void ThumbnailStore::RemoveFromDiskAtAndAboveIdTask( +void ThumbnailCache::RemoveFromDiskAtAndAboveIdTask( const base::FilePath& dir_path, TabId min_id) { base::FileEnumerator enumerator(dir_path, false, base::FileEnumerator::FILES); @@ -350,7 +350,7 @@ } } -void ThumbnailStore::WriteThumbnailIfNecessary( +void ThumbnailCache::WriteThumbnailIfNecessary( TabId tab_id, skia::RefPtr<SkPixelRef> compressed_data, float scale, @@ -361,10 +361,10 @@ write_tasks_count_++; base::Callback<void()> post_write_task = - base::Bind(&ThumbnailStore::PostWriteTask, weak_factory_.GetWeakPtr()); + base::Bind(&ThumbnailCache::PostWriteTask, weak_factory_.GetWeakPtr()); content::BrowserThread::PostTask(content::BrowserThread::FILE, FROM_HERE, - base::Bind(&ThumbnailStore::WriteTask, + base::Bind(&ThumbnailCache::WriteTask, GetFilePath(tab_id), compressed_data, scale, @@ -372,7 +372,7 @@ post_write_task)); } -void ThumbnailStore::CompressThumbnailIfNecessary( +void ThumbnailCache::CompressThumbnailIfNecessary( TabId tab_id, const base::Time& time_stamp, const SkBitmap& bitmap, @@ -385,7 +385,7 @@ compression_tasks_count_++; base::Callback<void(skia::RefPtr<SkPixelRef>, const gfx::Size&)> - post_compression_task = base::Bind(&ThumbnailStore::PostCompressionTask, + post_compression_task = base::Bind(&ThumbnailCache::PostCompressionTask, weak_factory_.GetWeakPtr(), tab_id, time_stamp, @@ -396,14 +396,14 @@ raw_data_size, ui_resource_provider_->SupportsETC1NonPowerOfTwo()); base::WorkerPool::PostTask(FROM_HERE, - base::Bind(&ThumbnailStore::CompressionTask, + base::Bind(&ThumbnailCache::CompressionTask, bitmap, encoded_size, post_compression_task), true); } -void ThumbnailStore::ReadNextThumbnail() { +void ThumbnailCache::ReadNextThumbnail() { if (read_queue_.empty() || read_in_progress_) return; @@ -414,15 +414,15 @@ base::Callback<void(skia::RefPtr<SkPixelRef>, float, const gfx::Size&)> post_read_task = base::Bind( - &ThumbnailStore::PostReadTask, weak_factory_.GetWeakPtr(), tab_id); + &ThumbnailCache::PostReadTask, weak_factory_.GetWeakPtr(), tab_id); content::BrowserThread::PostTask( content::BrowserThread::FILE, FROM_HERE, - base::Bind(&ThumbnailStore::ReadTask, false, file_path, post_read_task)); + base::Bind(&ThumbnailCache::ReadTask, false, file_path, post_read_task)); } -void ThumbnailStore::MakeSpaceForNewItemIfNecessary(TabId tab_id) { +void ThumbnailCache::MakeSpaceForNewItemIfNecessary(TabId tab_id) { if (cache_.Get(tab_id) || std::find(visible_ids_.begin(), visible_ids_.end(), tab_id) == visible_ids_.end() || @@ -462,14 +462,14 @@ cache_.Remove(key_to_remove); } -void ThumbnailStore::RemoveFromReadQueue(TabId tab_id) { +void ThumbnailCache::RemoveFromReadQueue(TabId tab_id) { TabIdList::iterator read_iter = std::find(read_queue_.begin(), read_queue_.end(), tab_id); if (read_iter != read_queue_.end()) read_queue_.erase(read_iter); } -void ThumbnailStore::InvalidateCachedThumbnail(Thumbnail* thumbnail) { +void ThumbnailCache::InvalidateCachedThumbnail(Thumbnail* thumbnail) { DCHECK(thumbnail); TabId tab_id = thumbnail->tab_id(); cc::UIResourceId uid = thumbnail->ui_resource_id(); @@ -483,7 +483,7 @@ approximation_cache_.Remove(tab_id); } -base::FilePath ThumbnailStore::GetFilePath(TabId tab_id) const { +base::FilePath ThumbnailCache::GetFilePath(TabId tab_id) const { return disk_cache_path_.Append(base::IntToString(tab_id)); } @@ -540,7 +540,7 @@ } // anonymous namespace -void ThumbnailStore::WriteTask(const base::FilePath& file_path, +void ThumbnailCache::WriteTask(const base::FilePath& file_path, skia::RefPtr<SkPixelRef> compressed_data, float scale, const gfx::Size& content_size, @@ -564,11 +564,11 @@ content::BrowserThread::UI, FROM_HERE, post_write_task); } -void ThumbnailStore::PostWriteTask() { +void ThumbnailCache::PostWriteTask() { write_tasks_count_--; } -void ThumbnailStore::CompressionTask( +void ThumbnailCache::CompressionTask( SkBitmap raw_data, gfx::Size encoded_size, const base::Callback<void(skia::RefPtr<SkPixelRef>, const gfx::Size&)>& @@ -618,7 +618,7 @@ base::Bind(post_compression_task, compressed_data, content_size)); } -void ThumbnailStore::PostCompressionTask( +void ThumbnailCache::PostCompressionTask( TabId tab_id, const base::Time& time_stamp, float scale, @@ -744,7 +744,7 @@ }// anonymous namespace -void ThumbnailStore::ReadTask( +void ThumbnailCache::ReadTask( bool decompress, const base::FilePath& file_path, const base::Callback< @@ -785,7 +785,7 @@ } } -void ThumbnailStore::PostReadTask(TabId tab_id, +void ThumbnailCache::PostReadTask(TabId tab_id, skia::RefPtr<SkPixelRef> compressed_data, float scale, const gfx::Size& content_size) { @@ -822,12 +822,12 @@ ReadNextThumbnail(); } -void ThumbnailStore::NotifyObserversOfThumbnailRead(TabId tab_id) { +void ThumbnailCache::NotifyObserversOfThumbnailRead(TabId tab_id) { FOR_EACH_OBSERVER( - ThumbnailStoreObserver, observers_, OnFinishedThumbnailRead(tab_id)); + ThumbnailCacheObserver, observers_, OnFinishedThumbnailRead(tab_id)); } -void ThumbnailStore::RemoveOnMatchedTimeStamp(TabId tab_id, +void ThumbnailCache::RemoveOnMatchedTimeStamp(TabId tab_id, const base::Time& time_stamp) { // We remove the cached version if it matches the tab_id and the time_stamp. Thumbnail* thumbnail = cache_.Get(tab_id); @@ -839,7 +839,7 @@ return; } -void ThumbnailStore::DecompressionTask( +void ThumbnailCache::DecompressionTask( const base::Callback<void(bool, SkBitmap)>& post_decompression_callback, skia::RefPtr<SkPixelRef> compressed_data, @@ -894,16 +894,16 @@ base::Bind(post_decompression_callback, success, raw_data_small)); } -ThumbnailStore::ThumbnailMetaData::ThumbnailMetaData() { +ThumbnailCache::ThumbnailMetaData::ThumbnailMetaData() { } -ThumbnailStore::ThumbnailMetaData::ThumbnailMetaData( +ThumbnailCache::ThumbnailMetaData::ThumbnailMetaData( const base::Time& current_time, const GURL& url) : capture_time_(current_time), url_(url) { } -std::pair<SkBitmap, float> ThumbnailStore::CreateApproximation( +std::pair<SkBitmap, float> ThumbnailCache::CreateApproximation( const SkBitmap& bitmap, float scale) { DCHECK(!bitmap.empty());
diff --git a/chrome/browser/android/thumbnail/thumbnail_store.h b/chrome/browser/android/thumbnail/thumbnail_cache.h similarity index 89% rename from chrome/browser/android/thumbnail/thumbnail_store.h rename to chrome/browser/android/thumbnail/thumbnail_cache.h index 8ad0c1a..098c190 100644 --- a/chrome/browser/android/thumbnail/thumbnail_store.h +++ b/chrome/browser/android/thumbnail/thumbnail_cache.h
@@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CHROME_BROWSER_ANDROID_THUMBNAIL_THUMBNAIL_STORE_H_ -#define CHROME_BROWSER_ANDROID_THUMBNAIL_THUMBNAIL_STORE_H_ +#ifndef CHROME_BROWSER_ANDROID_THUMBNAIL_THUMBNAIL_CACHE_H_ +#define CHROME_BROWSER_ANDROID_THUMBNAIL_THUMBNAIL_CACHE_H_ #include <list> #include <set> @@ -36,27 +36,27 @@ typedef std::list<TabId> TabIdList; -class ThumbnailStoreObserver { +class ThumbnailCacheObserver { public: virtual void OnFinishedThumbnailRead(TabId tab_id) = 0; }; -class ThumbnailStore : ThumbnailDelegate { +class ThumbnailCache : ThumbnailDelegate { public: - ThumbnailStore(const std::string& disk_cache_path_str, + ThumbnailCache(const std::string& disk_cache_path_str, size_t default_cache_size, size_t approximation_cache_size, size_t compression_queue_max_size, size_t write_queue_max_size, bool use_approximation_thumbnail); - ~ThumbnailStore() override; + ~ThumbnailCache() override; void SetUIResourceProvider(ui::UIResourceProvider* ui_resource_provider); - void AddThumbnailStoreObserver(ThumbnailStoreObserver* observer); - void RemoveThumbnailStoreObserver( - ThumbnailStoreObserver* observer); + void AddThumbnailCacheObserver(ThumbnailCacheObserver* observer); + void RemoveThumbnailCacheObserver( + ThumbnailCacheObserver* observer); void Put(TabId tab_id, const SkBitmap& bitmap, float thumbnail_scale); void Remove(TabId tab_id); @@ -154,16 +154,16 @@ ExpiringThumbnailCache cache_; ExpiringThumbnailCache approximation_cache_; - ObserverList<ThumbnailStoreObserver> observers_; + ObserverList<ThumbnailCacheObserver> observers_; ThumbnailMetaDataMap thumbnail_meta_data_; TabIdList read_queue_; TabIdList visible_ids_; ui::UIResourceProvider* ui_resource_provider_; - base::WeakPtrFactory<ThumbnailStore> weak_factory_; + base::WeakPtrFactory<ThumbnailCache> weak_factory_; - DISALLOW_COPY_AND_ASSIGN(ThumbnailStore); + DISALLOW_COPY_AND_ASSIGN(ThumbnailCache); }; -#endif // CHROME_BROWSER_ANDROID_THUMBNAIL_THUMBNAIL_STORE_H_ +#endif // CHROME_BROWSER_ANDROID_THUMBNAIL_THUMBNAIL_CACHE_H_
diff --git a/chrome/browser/android/webapps/single_tab_mode_tab_helper.cc b/chrome/browser/android/webapps/single_tab_mode_tab_helper.cc index 786075e..56db44f 100644 --- a/chrome/browser/android/webapps/single_tab_mode_tab_helper.cc +++ b/chrome/browser/android/webapps/single_tab_mode_tab_helper.cc
@@ -20,13 +20,13 @@ base::LazyInstance<SingleTabIDSet> g_blocked_ids = LAZY_INSTANCE_INITIALIZER; void AddPairOnIOThread(int32 process_id, int32 routing_id) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); RenderWidgetHostID single_tab_pair(process_id, routing_id); g_blocked_ids.Get().insert(single_tab_pair); } void RemovePairOnIOThread(int32 process_id, int32 routing_id) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); RenderWidgetHostID single_tab_pair(process_id, routing_id); SingleTabIDSet::iterator itr = g_blocked_ids.Get().find(single_tab_pair); DCHECK(itr != g_blocked_ids.Get().end()); @@ -87,7 +87,7 @@ bool SingleTabModeTabHelper::IsRegistered(int32 process_id, int32 routing_id) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); RenderWidgetHostID single_tab_pair(process_id, routing_id); SingleTabIDSet::iterator itr = g_blocked_ids.Get().find(single_tab_pair); return itr != g_blocked_ids.Get().end();
diff --git a/chrome/browser/app_icon_win.cc b/chrome/browser/app_icon_win.cc index d8b3628..6f1424e 100644 --- a/chrome/browser/app_icon_win.cc +++ b/chrome/browser/app_icon_win.cc
@@ -29,16 +29,18 @@ HICON GetAppIcon() { const int icon_id = GetAppIconResourceId(); + // HICON returned from LoadIcon do not leak and do not have to be destroyed. return LoadIcon(GetModuleHandle(chrome::kBrowserResourcesDll), MAKEINTRESOURCE(icon_id)); } HICON GetSmallAppIcon() { const int icon_id = GetAppIconResourceId(); + // HICON returned from LoadImage must be released using DestroyIcon. return static_cast<HICON>(LoadImage( GetModuleHandle(chrome::kBrowserResourcesDll), MAKEINTRESOURCE(icon_id), IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), - LR_DEFAULTCOLOR)); + LR_DEFAULTCOLOR | LR_SHARED)); } scoped_ptr<SkBitmap> GetAppIconForSize(int size) {
diff --git a/chrome/browser/apps/app_shim/app_shim_host_manager_mac.mm b/chrome/browser/apps/app_shim/app_shim_host_manager_mac.mm index 5d61239..0e3465d 100644 --- a/chrome/browser/apps/app_shim/app_shim_host_manager_mac.mm +++ b/chrome/browser/apps/app_shim/app_shim_host_manager_mac.mm
@@ -59,7 +59,7 @@ : did_init_(false) {} void AppShimHostManager::Init() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); DCHECK(!did_init_); did_init_ = true; apps::AppShimHandler::SetDefaultHandler(&extension_app_shim_handler_); @@ -90,7 +90,7 @@ } void AppShimHostManager::InitOnFileThread() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + DCHECK_CURRENTLY_ON(BrowserThread::FILE); base::FilePath user_data_dir; if (!PathService::Get(chrome::DIR_USER_DATA, &user_data_dir)) return; @@ -144,7 +144,7 @@ } void AppShimHostManager::ListenOnIOThread() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + DCHECK_CURRENTLY_ON(BrowserThread::IO); if (!acceptor_->Listen()) { BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, @@ -154,7 +154,7 @@ void AppShimHostManager::OnClientConnected( const IPC::ChannelHandle& handle) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + DCHECK_CURRENTLY_ON(BrowserThread::IO); BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, base::Bind(&CreateAppShimHost, handle));
diff --git a/chrome/browser/apps/app_url_redirector.cc b/chrome/browser/apps/app_url_redirector.cc index 623a7d0..cf15169 100644 --- a/chrome/browser/apps/app_url_redirector.cc +++ b/chrome/browser/apps/app_url_redirector.cc
@@ -37,7 +37,7 @@ const std::string& handler_id, content::WebContents* source, const navigation_interception::NavigationParams& params) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); // Redirect top-level navigations only. This excludes iframes and webviews // in particular.
diff --git a/chrome/browser/apps/drive/drive_app_converter.cc b/chrome/browser/apps/drive/drive_app_converter.cc index 7752c86..581b67c5 100644 --- a/chrome/browser/apps/drive/drive_app_converter.cc +++ b/chrome/browser/apps/drive/drive_app_converter.cc
@@ -37,9 +37,7 @@ IconFetcher(DriveAppConverter* converter, const GURL& icon_url, int expected_size) - : ImageRequest( - BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI)), - converter_(converter), + : converter_(converter), icon_url_(icon_url), expected_size_(expected_size) {} ~IconFetcher() override {}
diff --git a/chrome/browser/apps/ephemeral_app_throttle.cc b/chrome/browser/apps/ephemeral_app_throttle.cc index 1ebd54f..6260c51d 100644 --- a/chrome/browser/apps/ephemeral_app_throttle.cc +++ b/chrome/browser/apps/ephemeral_app_throttle.cc
@@ -30,7 +30,7 @@ const std::string& app_id, content::WebContents* source, const navigation_interception::NavigationParams& params) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); // Redirect top-level navigations only. if (source->IsSubframe())
diff --git a/chrome/browser/banners/app_banner_data_fetcher.cc b/chrome/browser/banners/app_banner_data_fetcher.cc index a32ba76..1897e81 100644 --- a/chrome/browser/banners/app_banner_data_fetcher.cc +++ b/chrome/browser/banners/app_banner_data_fetcher.cc
@@ -6,6 +6,7 @@ #include "base/bind.h" #include "base/strings/string_util.h" +#include "base/strings/utf_string_conversions.h" #include "chrome/browser/banners/app_banner_metrics.h" #include "chrome/browser/banners/app_banner_settings_helper.h" #include "chrome/browser/bitmap_fetcher/bitmap_fetcher.h" @@ -231,14 +232,23 @@ void AppBannerDataFetcher::OnDidGetManifest( const content::Manifest& manifest) { content::WebContents* web_contents = GetWebContents(); - if (!is_active_ || !web_contents) { + if (!is_active_ || !web_contents || manifest.IsEmpty()) { Cancel(); return; } - if (!IsManifestValid(manifest)) { - if (!weak_delegate_.get()->OnInvalidManifest(this)) - Cancel(); + if (manifest.prefer_related_applications && + manifest.related_applications.size()) { + for (const auto& application : manifest.related_applications) { + std::string platform = base::UTF16ToUTF8(application.platform.string()); + std::string id = base::UTF16ToUTF8(application.id.string()); + if (weak_delegate_->HandleNonWebApp(platform, application.url, id)) + return; + } + } + + if (!IsManifestValidForWebApp(manifest)) { + Cancel(); return; } @@ -336,7 +346,7 @@ } // static -bool AppBannerDataFetcher::IsManifestValid( +bool AppBannerDataFetcher::IsManifestValidForWebApp( const content::Manifest& manifest) { if (manifest.IsEmpty()) return false;
diff --git a/chrome/browser/banners/app_banner_data_fetcher.h b/chrome/browser/banners/app_banner_data_fetcher.h index 085e920..ce6d8e30 100644 --- a/chrome/browser/banners/app_banner_data_fetcher.h +++ b/chrome/browser/banners/app_banner_data_fetcher.h
@@ -43,9 +43,11 @@ class Delegate { public: - // Called when no valid manifest was found. Returns |true| if the fetcher - // needs to remain active and wait for a callback. - virtual bool OnInvalidManifest(AppBannerDataFetcher* fetcher) = 0; + // Called to handle a non-web app. Returns |true| if the non-web app can be + // handled, and the fetcher needs to remain active and wait for a callback. + virtual bool HandleNonWebApp(const std::string& platform, + const GURL& url, + const std::string& id) = 0; }; // Returns the current time. @@ -137,7 +139,7 @@ // Returns whether the given Manifest is following the requirements to show // a web app banner. - static bool IsManifestValid(const content::Manifest& manifest); + static bool IsManifestValidForWebApp(const content::Manifest& manifest); const int ideal_icon_size_; const base::WeakPtr<Delegate> weak_delegate_;
diff --git a/chrome/browser/banners/app_banner_data_fetcher_browsertest.cc b/chrome/browser/banners/app_banner_data_fetcher_browsertest.cc index 0d119c6..c72e861 100644 --- a/chrome/browser/banners/app_banner_data_fetcher_browsertest.cc +++ b/chrome/browser/banners/app_banner_data_fetcher_browsertest.cc
@@ -54,8 +54,7 @@ class AppBannerDataFetcherBrowserTest : public InProcessBrowserTest, public AppBannerDataFetcher::Delegate { public: - AppBannerDataFetcherBrowserTest() : manifest_was_invalid_(false), - weak_factory_(this) { + AppBannerDataFetcherBrowserTest() : weak_factory_(this) { } void SetUpOnMainThread() override { @@ -63,9 +62,11 @@ InProcessBrowserTest::SetUpOnMainThread(); } - bool OnInvalidManifest(AppBannerDataFetcher* fetcher) override { + bool HandleNonWebApp(const std::string& platform, + const GURL& url, + const std::string& id) override { base::MessageLoop::current()->PostTask(FROM_HERE, quit_closure_); - manifest_was_invalid_ = true; + non_web_platform_ = platform; return false; } @@ -76,7 +77,7 @@ protected: void RunFetcher(const GURL& url, - bool expected_manifest_valid, + const std::string& expected_non_web_platform, bool expected_to_show) { content::WebContents* web_contents = browser()->tab_strip_model()->GetActiveWebContents(); @@ -91,7 +92,7 @@ fetcher->Start(url); run_loop.Run(); - EXPECT_EQ(expected_manifest_valid, !manifest_was_invalid_); + EXPECT_EQ(expected_non_web_platform, non_web_platform_); EXPECT_EQ(expected_to_show, observer->will_show()); ASSERT_FALSE(fetcher->is_active()); } @@ -106,54 +107,69 @@ } private: - bool manifest_was_invalid_; + std::string non_web_platform_; base::Closure quit_closure_; base::WeakPtrFactory<AppBannerDataFetcherBrowserTest> weak_factory_; }; IN_PROC_BROWSER_TEST_F(AppBannerDataFetcherBrowserTest, WebAppBannerCreated) { - std::string valid_page = "/banners/manifest_test_page.html"; + std::string valid_page("/banners/manifest_test_page.html"); GURL test_url = embedded_test_server()->GetURL(valid_page); content::WebContents* web_contents = browser()->tab_strip_model()->GetActiveWebContents(); LoadURLAndWaitForServiceWorker(test_url); - RunFetcher(web_contents->GetURL(), true, false); + RunFetcher(web_contents->GetURL(), std::string(), false); // Advance by a day, then visit the page again to trigger the banner. AppBannerDataFetcher::SetTimeDeltaForTesting(1); LoadURLAndWaitForServiceWorker(test_url); - RunFetcher(web_contents->GetURL(), true, true); + RunFetcher(web_contents->GetURL(), std::string(), true); +} + +IN_PROC_BROWSER_TEST_F(AppBannerDataFetcherBrowserTest, PlayAppManifest) { + std::string valid_page("/banners/play_app_test_page.html"); + GURL test_url = embedded_test_server()->GetURL(valid_page); + content::WebContents* web_contents = + browser()->tab_strip_model()->GetActiveWebContents(); + + // Native banners do not require the SW, so we can just load the URL. + ui_test_utils::NavigateToURL(browser(), test_url); + std::string play_platform("play"); + RunFetcher(web_contents->GetURL(), play_platform, false); + + // The logic to get the details for a play app banner are only on android + // builds, so this test does not check that the banner is shown. } IN_PROC_BROWSER_TEST_F(AppBannerDataFetcherBrowserTest, NoManifest) { - std::string valid_page = "/banners/no_manifest_test_page.html"; + std::string valid_page("/banners/no_manifest_test_page.html"); GURL test_url = embedded_test_server()->GetURL(valid_page); content::WebContents* web_contents = browser()->tab_strip_model()->GetActiveWebContents(); LoadURLAndWaitForServiceWorker(test_url); - RunFetcher(web_contents->GetURL(), false, false); + RunFetcher(web_contents->GetURL(), std::string(), false); // Advance by a day, then visit the page again. Still shouldn't see a banner. AppBannerDataFetcher::SetTimeDeltaForTesting(1); LoadURLAndWaitForServiceWorker(test_url); - RunFetcher(web_contents->GetURL(), false, false); + RunFetcher(web_contents->GetURL(), std::string(), false); } IN_PROC_BROWSER_TEST_F(AppBannerDataFetcherBrowserTest, CancelBanner) { - std::string valid_page = "/banners/cancel_test_page.html"; + std::string valid_page("/banners/cancel_test_page.html"); GURL test_url = embedded_test_server()->GetURL(valid_page); content::WebContents* web_contents = browser()->tab_strip_model()->GetActiveWebContents(); LoadURLAndWaitForServiceWorker(test_url); - RunFetcher(web_contents->GetURL(), true, false); + RunFetcher(web_contents->GetURL(), std::string(), false); // Advance by a day, then visit the page again. Still shouldn't see a banner. AppBannerDataFetcher::SetTimeDeltaForTesting(1); LoadURLAndWaitForServiceWorker(test_url); - RunFetcher(web_contents->GetURL(), true, false); + RunFetcher(web_contents->GetURL(), std::string(), false); } } // namespace banners
diff --git a/chrome/browser/banners/app_banner_data_fetcher_unittest.cc b/chrome/browser/banners/app_banner_data_fetcher_unittest.cc index d662c80..2254130 100644 --- a/chrome/browser/banners/app_banner_data_fetcher_unittest.cc +++ b/chrome/browser/banners/app_banner_data_fetcher_unittest.cc
@@ -33,7 +33,7 @@ } static bool IsManifestValid(const content::Manifest& manifest) { - return AppBannerDataFetcher::IsManifestValid(manifest); + return AppBannerDataFetcher::IsManifestValidForWebApp(manifest); } };
diff --git a/chrome/browser/banners/app_banner_manager.cc b/chrome/browser/banners/app_banner_manager.cc index 67eae4f..6fdd5cf1 100644 --- a/chrome/browser/banners/app_banner_manager.cc +++ b/chrome/browser/banners/app_banner_manager.cc
@@ -59,7 +59,9 @@ } -bool AppBannerManager::OnInvalidManifest(AppBannerDataFetcher* fetcher) { +bool AppBannerManager::HandleNonWebApp(const std::string& platform, + const GURL& url, + const std::string& id) { return false; }
diff --git a/chrome/browser/banners/app_banner_manager.h b/chrome/browser/banners/app_banner_manager.h index 020c559..a84be60 100644 --- a/chrome/browser/banners/app_banner_manager.h +++ b/chrome/browser/banners/app_banner_manager.h
@@ -43,9 +43,6 @@ void DidFinishLoad(content::RenderFrameHost* render_frame_host, const GURL& validated_url) override; - // AppBannerDataFetcher::Delegate overrides. - bool OnInvalidManifest(AppBannerDataFetcher* fetcher) override; - protected: void ReplaceWebContents(content::WebContents* web_contents); @@ -61,6 +58,11 @@ int ideal_icon_size() { return ideal_icon_size_; } private: + // AppBannerDataFetcher::Delegate overrides. + bool HandleNonWebApp(const std::string& platform, + const GURL& url, + const std::string& id) override; + // Called after the manager sends a message to the renderer regarding its // intention to show a prompt. The renderer will send a message back with the // opportunity to cancel.
diff --git a/chrome/browser/bitmap_fetcher/bitmap_fetcher.cc b/chrome/browser/bitmap_fetcher/bitmap_fetcher.cc index e413fa1..e5d189a1 100644 --- a/chrome/browser/bitmap_fetcher/bitmap_fetcher.cc +++ b/chrome/browser/bitmap_fetcher/bitmap_fetcher.cc
@@ -12,9 +12,7 @@ namespace chrome { BitmapFetcher::BitmapFetcher(const GURL& url, BitmapFetcherDelegate* delegate) - : ImageRequest(content::BrowserThread::GetMessageLoopProxyForThread( - content::BrowserThread::UI)), - url_(url), + : url_(url), delegate_(delegate) { }
diff --git a/chrome/browser/bitmap_fetcher/bitmap_fetcher_service_unittest.cc b/chrome/browser/bitmap_fetcher/bitmap_fetcher_service_unittest.cc index 73513c8..a63d7d2 100644 --- a/chrome/browser/bitmap_fetcher/bitmap_fetcher_service_unittest.cc +++ b/chrome/browser/bitmap_fetcher/bitmap_fetcher_service_unittest.cc
@@ -6,6 +6,7 @@ #include "chrome/browser/bitmap_fetcher/bitmap_fetcher.h" #include "chrome/test/base/testing_profile.h" +#include "content/public/test/test_browser_thread_bundle.h" #include "testing/gtest/include/gtest/gtest.h" namespace { @@ -27,7 +28,10 @@ target_->OnImageChanged(); } + private: TestNotificationInterface* target_; + + DISALLOW_COPY_AND_ASSIGN(TestObserver); }; class TestService : public BitmapFetcherService { @@ -48,30 +52,33 @@ class BitmapFetcherServiceTest : public testing::Test, public TestNotificationInterface { public: + BitmapFetcherServiceTest() + : url1_(GURL("http://example.org/sample-image-1.png")), + url2_(GURL("http://example.org/sample-image-2.png")) { + } + void SetUp() override { service_.reset(new TestService(&profile_)); - requestsFinished_ = 0; - imagesChanged_ = 0; - url1_ = GURL("http://example.org/sample-image-1.png"); - url2_ = GURL("http://example.org/sample-image-2.png"); + requests_finished_ = 0; + images_changed_ = 0; } - const ScopedVector<BitmapFetcherRequest>& requests() { + const ScopedVector<BitmapFetcherRequest>& requests() const { return service_->requests_; } - const ScopedVector<chrome::BitmapFetcher>& active_fetchers() { + const ScopedVector<chrome::BitmapFetcher>& active_fetchers() const { return service_->active_fetchers_; } - size_t cache_size() { return service_->cache_.size(); } + size_t cache_size() const { return service_->cache_.size(); } - void OnImageChanged() override { imagesChanged_++; } + void OnImageChanged() override { images_changed_++; } - void OnRequestFinished() override { requestsFinished_++; } + void OnRequestFinished() override { requests_finished_++; } // Simulate finishing a URL fetch and decode for the given fetcher. void CompleteFetch(const GURL& url) { const chrome::BitmapFetcher* fetcher = service_->FindFetcherForUrl(url); - ASSERT_TRUE(NULL != fetcher); + ASSERT_TRUE(fetcher); // Create a non-empty bitmap. SkBitmap image; @@ -83,20 +90,21 @@ void FailFetch(const GURL& url) { const chrome::BitmapFetcher* fetcher = service_->FindFetcherForUrl(url); - ASSERT_TRUE(NULL != fetcher); + ASSERT_TRUE(fetcher); const_cast<chrome::BitmapFetcher*>(fetcher)->OnImageDecoded(SkBitmap()); } protected: scoped_ptr<BitmapFetcherService> service_; - int imagesChanged_; - int requestsFinished_; + int images_changed_; + int requests_finished_; - GURL url1_; - GURL url2_; + const GURL url1_; + const GURL url2_; private: + content::TestBrowserThreadBundle thread_bundle_; TestingProfile profile_; }; @@ -135,8 +143,8 @@ EXPECT_EQ(4U, requests().size()); CompleteFetch(url1_); - EXPECT_EQ(4, imagesChanged_); - EXPECT_EQ(4, requestsFinished_); + EXPECT_EQ(4, images_changed_); + EXPECT_EQ(4, requests_finished_); } TEST_F(BitmapFetcherServiceTest, CancelRequest) { @@ -152,10 +160,10 @@ EXPECT_EQ(4U, requests().size()); CompleteFetch(url2_); - EXPECT_EQ(0, imagesChanged_); + EXPECT_EQ(0, images_changed_); CompleteFetch(url1_); - EXPECT_EQ(4, imagesChanged_); + EXPECT_EQ(4, images_changed_); } TEST_F(BitmapFetcherServiceTest, FailedRequestsDontEnterCache) {
diff --git a/chrome/browser/captive_portal/captive_portal_tab_helper.cc b/chrome/browser/captive_portal/captive_portal_tab_helper.cc index 2b63c03..8aa0b309 100644 --- a/chrome/browser/captive_portal/captive_portal_tab_helper.cc +++ b/chrome/browser/captive_portal/captive_portal_tab_helper.cc
@@ -93,7 +93,7 @@ provisional_render_view_host_ = render_view_host; pending_error_code_ = net::OK; - tab_reloader_->OnLoadStart(validated_url.SchemeUsesTLS()); + tab_reloader_->OnLoadStart(validated_url.SchemeIsCryptographic()); } void CaptivePortalTabHelper::DidCommitProvisionalLoadForFrame( @@ -115,7 +115,7 @@ OnLoadAborted(); // Send information about the new load. - tab_reloader_->OnLoadStart(url.SchemeUsesTLS()); + tab_reloader_->OnLoadStart(url.SchemeIsCryptographic()); tab_reloader_->OnLoadCommitted(net::OK); } @@ -239,7 +239,7 @@ return; } - tab_reloader_->OnRedirect(new_url.SchemeUsesTLS()); + tab_reloader_->OnRedirect(new_url.SchemeIsCryptographic()); } void CaptivePortalTabHelper::OnCaptivePortalResults(
diff --git a/chrome/browser/captive_portal/captive_portal_tab_helper_unittest.cc b/chrome/browser/captive_portal/captive_portal_tab_helper_unittest.cc index eab6ca1..ce0c9c7 100644 --- a/chrome/browser/captive_portal/captive_portal_tab_helper_unittest.cc +++ b/chrome/browser/captive_portal/captive_portal_tab_helper_unittest.cc
@@ -87,7 +87,8 @@ // Simulates a successful load of |url|. void SimulateSuccess(const GURL& url, content::RenderViewHost* render_view_host) { - EXPECT_CALL(mock_reloader(), OnLoadStart(url.SchemeUsesTLS())).Times(1); + EXPECT_CALL(mock_reloader(), OnLoadStart(url.SchemeIsCryptographic())) + .Times(1); tab_helper().DidStartProvisionalLoadForFrame( render_view_host->GetMainFrame(), url, false, false); @@ -101,7 +102,8 @@ // Simulates a connection timeout while requesting |url|. void SimulateTimeout(const GURL& url, content::RenderViewHost* render_view_host) { - EXPECT_CALL(mock_reloader(), OnLoadStart(url.SchemeUsesTLS())).Times(1); + EXPECT_CALL(mock_reloader(), OnLoadStart(url.SchemeIsCryptographic())) + .Times(1); tab_helper().DidStartProvisionalLoadForFrame( render_view_host->GetMainFrame(), url, false, false); @@ -125,7 +127,8 @@ void SimulateAbort(const GURL& url, content::RenderViewHost* render_view_host, NavigationType navigation_type) { - EXPECT_CALL(mock_reloader(), OnLoadStart(url.SchemeUsesTLS())).Times(1); + EXPECT_CALL(mock_reloader(), OnLoadStart(url.SchemeIsCryptographic())) + .Times(1); tab_helper().DidStartProvisionalLoadForFrame( render_view_host->GetMainFrame(), url, false, false); @@ -151,7 +154,8 @@ void SimulateAbortTimeout(const GURL& url, content::RenderViewHost* render_view_host, NavigationType navigation_type) { - EXPECT_CALL(mock_reloader(), OnLoadStart(url.SchemeUsesTLS())).Times(1); + EXPECT_CALL(mock_reloader(), OnLoadStart(url.SchemeIsCryptographic())) + .Times(1); tab_helper().DidStartProvisionalLoadForFrame( render_view_host->GetMainFrame(), url, false, false); @@ -334,7 +338,7 @@ // A same-site load for the original RenderViewHost starts. EXPECT_CALL(mock_reloader(), - OnLoadStart(same_site_url.SchemeUsesTLS())).Times(1); + OnLoadStart(same_site_url.SchemeIsCryptographic())).Times(1); tab_helper().DidStartProvisionalLoadForFrame( main_render_frame1(), same_site_url, false, false); @@ -343,7 +347,7 @@ // for the old navigation. EXPECT_CALL(mock_reloader(), OnAbort()).Times(1); EXPECT_CALL(mock_reloader(), - OnLoadStart(cross_process_url.SchemeUsesTLS())).Times(1); + OnLoadStart(cross_process_url.SchemeIsCryptographic())).Times(1); tab_helper().DidStartProvisionalLoadForFrame( main_render_frame2(), cross_process_url, false, false); @@ -378,7 +382,7 @@ // A same-site load for the original RenderViewHost starts. EXPECT_CALL(mock_reloader(), - OnLoadStart(same_site_url.SchemeUsesTLS())).Times(1); + OnLoadStart(same_site_url.SchemeIsCryptographic())).Times(1); tab_helper().DidStartProvisionalLoadForFrame( main_render_frame1(), same_site_url, false, false); @@ -387,7 +391,7 @@ // for the old navigation. EXPECT_CALL(mock_reloader(), OnAbort()).Times(1); EXPECT_CALL(mock_reloader(), - OnLoadStart(cross_process_url.SchemeUsesTLS())).Times(1); + OnLoadStart(cross_process_url.SchemeIsCryptographic())).Times(1); tab_helper().DidStartProvisionalLoadForFrame( main_render_frame2(), cross_process_url, false, false); @@ -400,7 +404,7 @@ // The same-site navigation succeeds. EXPECT_CALL(mock_reloader(), OnAbort()).Times(1); EXPECT_CALL(mock_reloader(), - OnLoadStart(same_site_url.SchemeUsesTLS())).Times(1); + OnLoadStart(same_site_url.SchemeIsCryptographic())).Times(1); EXPECT_CALL(mock_reloader(), OnLoadCommitted(net::OK)).Times(1); tab_helper().DidCommitProvisionalLoadForFrame( main_render_frame1(), same_site_url, ui::PAGE_TRANSITION_LINK); @@ -450,7 +454,8 @@ ->AppendChild("subframe"); // Loads start. - EXPECT_CALL(mock_reloader(), OnLoadStart(url.SchemeUsesTLS())).Times(1); + EXPECT_CALL(mock_reloader(), OnLoadStart(url.SchemeIsCryptographic())) + .Times(1); tab_helper().DidStartProvisionalLoadForFrame( main_render_frame1(), url, false, false); tab_helper().DidStartProvisionalLoadForFrame(subframe, url, false, false); @@ -505,13 +510,14 @@ // Simulates an HTTPS to HTTP redirect. TEST_F(CaptivePortalTabHelperTest, HttpsToHttpRedirect) { GURL https_url(kHttpsUrl); - EXPECT_CALL(mock_reloader(), - OnLoadStart(https_url.SchemeUsesTLS())).Times(1); + EXPECT_CALL(mock_reloader(), OnLoadStart(https_url.SchemeIsCryptographic())) + .Times(1); tab_helper().DidStartProvisionalLoadForFrame( main_render_frame1(), https_url, false, false); GURL http_url(kHttpUrl); - EXPECT_CALL(mock_reloader(), OnRedirect(http_url.SchemeUsesTLS())).Times(1); + EXPECT_CALL(mock_reloader(), OnRedirect(http_url.SchemeIsCryptographic())) + .Times(1); OnRedirect(content::RESOURCE_TYPE_MAIN_FRAME, http_url, render_view_host1()->GetProcess()->GetID()); @@ -523,12 +529,13 @@ // Simulates an HTTPS to HTTPS redirect. TEST_F(CaptivePortalTabHelperTest, HttpToHttpRedirect) { GURL http_url(kHttpUrl); - EXPECT_CALL(mock_reloader(), - OnLoadStart(http_url.SchemeUsesTLS())).Times(1); + EXPECT_CALL(mock_reloader(), OnLoadStart(http_url.SchemeIsCryptographic())) + .Times(1); tab_helper().DidStartProvisionalLoadForFrame( main_render_frame1(), http_url, false, false); - EXPECT_CALL(mock_reloader(), OnRedirect(http_url.SchemeUsesTLS())).Times(1); + EXPECT_CALL(mock_reloader(), OnRedirect(http_url.SchemeIsCryptographic())) + .Times(1); OnRedirect(content::RESOURCE_TYPE_MAIN_FRAME, http_url, render_view_host1()->GetProcess()->GetID());
diff --git a/chrome/browser/chrome_browser_main.cc b/chrome/browser/chrome_browser_main.cc index a76aad9..a8c1b1f 100644 --- a/chrome/browser/chrome_browser_main.cc +++ b/chrome/browser/chrome_browser_main.cc
@@ -553,18 +553,6 @@ DISALLOW_COPY_AND_ASSIGN(LoadCompleteListener); }; -base::StackSamplingProfiler::SamplingParams GetStartupSamplingParams() { - // Sample at 10Hz for 30 seconds. - base::StackSamplingProfiler::SamplingParams params; - params.initial_delay = base::TimeDelta::FromMilliseconds(0); - params.bursts = 1; - params.samples_per_burst = 300; - params.sampling_interval = base::TimeDelta::FromMilliseconds(100); - params.preserve_sample_ordering = false; - params.user_data = metrics::CallStackProfileMetricsProvider::PROCESS_STARTUP; - return params; -} - } // namespace namespace chrome_browser { @@ -593,14 +581,11 @@ startup_watcher_(new StartupTimeBomb()), shutdown_watcher_(new ShutdownWatcherHelper()), browser_field_trials_(parameters.command_line), - sampling_profiler_(base::PlatformThread::CurrentId(), - GetStartupSamplingParams()), profile_(NULL), run_message_loop_(true), notify_result_(ProcessSingleton::PROCESS_NONE), local_state_(NULL), restart_last_session_(false) { - sampling_profiler_.Start(); // If we're running tests (ui_task is non-null). if (parameters.ui_task) browser_defaults::enable_help_app = false;
diff --git a/chrome/browser/chrome_browser_main.h b/chrome/browser/chrome_browser_main.h index 02e5d58..b0fd85ef 100644 --- a/chrome/browser/chrome_browser_main.h +++ b/chrome/browser/chrome_browser_main.h
@@ -8,7 +8,6 @@ #include "base/basictypes.h" #include "base/memory/scoped_ptr.h" #include "base/metrics/field_trial.h" -#include "base/profiler/stack_sampling_profiler.h" #include "base/tracked_objects.h" #include "chrome/browser/chrome_browser_field_trials.h" #include "chrome/browser/chrome_process_singleton.h" @@ -144,10 +143,6 @@ // Parts are deleted in the inverse order they are added. std::vector<ChromeBrowserMainExtraParts*> chrome_extra_parts_; - // A profiler that periodically samples stack traces. Used to sample startup - // behavior. - base::StackSamplingProfiler sampling_profiler_; - // Members initialized after / released before main_message_loop_ ------------ scoped_ptr<BrowserProcessImpl> browser_process_;
diff --git a/chrome/browser/chromeos/app_mode/certificate_manager_dialog.cc b/chrome/browser/chromeos/app_mode/certificate_manager_dialog.cc index 516f87b9..7546ae3 100644 --- a/chrome/browser/chromeos/app_mode/certificate_manager_dialog.cc +++ b/chrome/browser/chromeos/app_mode/certificate_manager_dialog.cc
@@ -43,7 +43,7 @@ window, base::string16(), GURL(chrome::kChromeUICertificateManagerDialogURL)) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); gfx::Rect screen_bounds(chromeos::CalculateScreenBounds(gfx::Size())); SetDialogSize(CalculateSize(screen_bounds.width(),
diff --git a/chrome/browser/chromeos/app_mode/kiosk_app_manager.cc b/chrome/browser/chromeos/app_mode/kiosk_app_manager.cc index ab182b58..a37df89e 100644 --- a/chrome/browser/chromeos/app_mode/kiosk_app_manager.cc +++ b/chrome/browser/chromeos/app_mode/kiosk_app_manager.cc
@@ -23,6 +23,7 @@ #include "chrome/browser/chromeos/app_mode/kiosk_app_external_loader.h" #include "chrome/browser/chromeos/app_mode/kiosk_app_manager_observer.h" #include "chrome/browser/chromeos/app_mode/kiosk_external_updater.h" +#include "chrome/browser/chromeos/ownership/owner_settings_service_chromeos.h" #include "chrome/browser/chromeos/ownership/owner_settings_service_chromeos_factory.h" #include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h" #include "chrome/browser/chromeos/policy/device_local_account.h" @@ -183,20 +184,20 @@ return auto_launch_app_id_; } -void KioskAppManager::SetAutoLaunchApp(const std::string& app_id) { +void KioskAppManager::SetAutoLaunchApp(const std::string& app_id, + OwnerSettingsServiceChromeOS* service) { SetAutoLoginState(AUTOLOGIN_REQUESTED); // Clean first, so the proper change callbacks are triggered even // if we are only changing AutoLoginState here. if (!auto_launch_app_id_.empty()) { - CrosSettings::Get()->SetString(kAccountsPrefDeviceLocalAccountAutoLoginId, - std::string()); + service->SetString(kAccountsPrefDeviceLocalAccountAutoLoginId, + std::string()); } - CrosSettings::Get()->SetString( + service->SetString( kAccountsPrefDeviceLocalAccountAutoLoginId, app_id.empty() ? std::string() : GenerateKioskAppAccountId(app_id)); - CrosSettings::Get()->SetInteger( - kAccountsPrefDeviceLocalAccountAutoLoginDelay, 0); + service->SetInteger(kAccountsPrefDeviceLocalAccountAutoLoginDelay, 0); } void KioskAppManager::SetAppWasAutoLaunchedWithZeroDelay( @@ -332,7 +333,8 @@ return GetAutoLoginState() == AUTOLOGIN_APPROVED; } -void KioskAppManager::AddApp(const std::string& app_id) { +void KioskAppManager::AddApp(const std::string& app_id, + OwnerSettingsServiceChromeOS* service) { std::vector<policy::DeviceLocalAccount> device_local_accounts = policy::GetDeviceLocalAccounts(CrosSettings::Get()); @@ -353,13 +355,14 @@ app_id, std::string())); - policy::SetDeviceLocalAccounts(CrosSettings::Get(), device_local_accounts); + policy::SetDeviceLocalAccounts(service, device_local_accounts); } -void KioskAppManager::RemoveApp(const std::string& app_id) { +void KioskAppManager::RemoveApp(const std::string& app_id, + OwnerSettingsServiceChromeOS* service) { // Resets auto launch app if it is the removed app. if (auto_launch_app_id_ == app_id) - SetAutoLaunchApp(std::string()); + SetAutoLaunchApp(std::string(), service); std::vector<policy::DeviceLocalAccount> device_local_accounts = policy::GetDeviceLocalAccounts(CrosSettings::Get()); @@ -377,7 +380,7 @@ } } - policy::SetDeviceLocalAccounts(CrosSettings::Get(), device_local_accounts); + policy::SetDeviceLocalAccounts(service, device_local_accounts); } void KioskAppManager::GetApps(Apps* apps) const {
diff --git a/chrome/browser/chromeos/app_mode/kiosk_app_manager.h b/chrome/browser/chromeos/app_mode/kiosk_app_manager.h index aff5898..72d511c 100644 --- a/chrome/browser/chromeos/app_mode/kiosk_app_manager.h +++ b/chrome/browser/chromeos/app_mode/kiosk_app_manager.h
@@ -38,6 +38,7 @@ class KioskAppExternalLoader; class KioskAppManagerObserver; class KioskExternalUpdater; +class OwnerSettingsServiceChromeOS; // KioskAppManager manages cached app data. class KioskAppManager : public KioskAppDataDelegate, @@ -121,7 +122,8 @@ std::string GetAutoLaunchApp() const; // Sets |app_id| as the app to auto launch at start up. - void SetAutoLaunchApp(const std::string& app_id); + void SetAutoLaunchApp(const std::string& app_id, + OwnerSettingsServiceChromeOS* service); // Returns true if there is a pending auto-launch request. bool IsAutoLaunchRequested() const; @@ -134,8 +136,9 @@ // Adds/removes a kiosk app by id. When removed, all locally cached data // will be removed as well. - void AddApp(const std::string& app_id); - void RemoveApp(const std::string& app_id); + void AddApp(const std::string& app_id, OwnerSettingsServiceChromeOS* service); + void RemoveApp(const std::string& app_id, + OwnerSettingsServiceChromeOS* service); // Gets info of all apps that have no meta data load error. void GetApps(Apps* apps) const;
diff --git a/chrome/browser/chromeos/app_mode/kiosk_app_manager_browsertest.cc b/chrome/browser/chromeos/app_mode/kiosk_app_manager_browsertest.cc index 48498b6..16f85d0 100644 --- a/chrome/browser/chromeos/app_mode/kiosk_app_manager_browsertest.cc +++ b/chrome/browser/chromeos/app_mode/kiosk_app_manager_browsertest.cc
@@ -7,6 +7,7 @@ #include "base/command_line.h" #include "base/files/file_util.h" #include "base/files/scoped_temp_dir.h" +#include "base/memory/scoped_ptr.h" #include "base/message_loop/message_loop.h" #include "base/path_service.h" #include "base/prefs/scoped_user_pref_update.h" @@ -15,9 +16,10 @@ #include "chrome/browser/browser_process.h" #include "chrome/browser/chromeos/app_mode/fake_cws.h" #include "chrome/browser/chromeos/app_mode/kiosk_app_manager_observer.h" +#include "chrome/browser/chromeos/ownership/fake_owner_settings_service.h" #include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h" #include "chrome/browser/chromeos/policy/device_local_account.h" -#include "chrome/browser/chromeos/settings/cros_settings.h" +#include "chrome/browser/chromeos/settings/scoped_cros_settings_test_helper.h" #include "chrome/browser/ui/browser.h" #include "chrome/common/chrome_paths.h" #include "chrome/common/chrome_switches.h" @@ -165,7 +167,7 @@ class KioskAppManagerTest : public InProcessBrowserTest { public: - KioskAppManagerTest() : fake_cws_(new FakeCWS()) {} + KioskAppManagerTest() : settings_helper_(false), fake_cws_(new FakeCWS()) {} ~KioskAppManagerTest() override {} // InProcessBrowserTest overrides: @@ -195,8 +197,14 @@ // Restart the thread as the sandbox host process has already been spawned. embedded_test_server()->RestartThreadAndListen(); + + settings_helper_.ReplaceProvider(kAccountsPrefDeviceLocalAccounts); + owner_settings_service_ = + settings_helper_.CreateOwnerSettingsService(browser()->profile()); } + void TearDownOnMainThread() override { settings_helper_.RestoreProvider(); } + void SetUpInProcessBrowserTestFixture() override { InProcessBrowserTest::SetUpInProcessBrowserTestFixture(); @@ -270,8 +278,8 @@ kAccountsPrefDeviceLocalAccountsKeyKioskAppId, app_id); device_local_accounts.Append(entry.release()); - CrosSettings::Get()->Set(kAccountsPrefDeviceLocalAccounts, - device_local_accounts); + owner_settings_service_->Set(kAccountsPrefDeviceLocalAccounts, + device_local_accounts); } bool GetCachedCrx(const std::string& app_id, @@ -289,7 +297,7 @@ fake_cws_->SetUpdateCrx(id, crx_file_name, version); AppDataLoadWaiter waiter(manager(), 3); - manager()->AddApp(id); + manager()->AddApp(id, owner_settings_service_.get()); waiter.Wait(); EXPECT_TRUE(waiter.loaded()); @@ -342,6 +350,10 @@ KioskAppManager* manager() const { return KioskAppManager::Get(); } FakeCWS* fake_cws() { return fake_cws_.get(); } + protected: + ScopedCrosSettingsTestHelper settings_helper_; + scoped_ptr<FakeOwnerSettingsService> owner_settings_service_; + private: base::ScopedTempDir temp_dir_; scoped_ptr<FakeCWS> fake_cws_; @@ -353,12 +365,12 @@ // Add a couple of apps. Use "fake_app_x" that do not have data on the test // server to avoid pending data loads that could be lingering on tear down and // cause DCHECK failure in utility_process_host_impl.cc. - manager()->AddApp("fake_app_1"); - manager()->AddApp("fake_app_2"); + manager()->AddApp("fake_app_1", owner_settings_service_.get()); + manager()->AddApp("fake_app_2", owner_settings_service_.get()); EXPECT_EQ("fake_app_1,fake_app_2", GetAppIds()); // Set an auto launch app. - manager()->SetAutoLaunchApp("fake_app_1"); + manager()->SetAutoLaunchApp("fake_app_1", owner_settings_service_.get()); EXPECT_EQ("fake_app_1", manager()->GetAutoLaunchApp()); // Make sure that if an app was auto launched with zero delay, it is reflected @@ -372,7 +384,7 @@ EXPECT_TRUE(app.was_auto_launched_with_zero_delay); // Clear the auto launch app. - manager()->SetAutoLaunchApp(""); + manager()->SetAutoLaunchApp("", owner_settings_service_.get()); EXPECT_EQ("", manager()->GetAutoLaunchApp()); EXPECT_FALSE(manager()->IsAutoLaunchEnabled()); @@ -382,7 +394,7 @@ EXPECT_TRUE(app.was_auto_launched_with_zero_delay); // Set another auto launch app. - manager()->SetAutoLaunchApp("fake_app_2"); + manager()->SetAutoLaunchApp("fake_app_2", owner_settings_service_.get()); EXPECT_EQ("fake_app_2", manager()->GetAutoLaunchApp()); // Check auto launch permissions. @@ -391,24 +403,24 @@ EXPECT_TRUE(manager()->IsAutoLaunchEnabled()); // Remove the auto launch app. - manager()->RemoveApp("fake_app_2"); + manager()->RemoveApp("fake_app_2", owner_settings_service_.get()); EXPECT_EQ("fake_app_1", GetAppIds()); EXPECT_EQ("", manager()->GetAutoLaunchApp()); // Add the just removed auto launch app again and it should no longer be // the auto launch app. - manager()->AddApp("fake_app_2"); + manager()->AddApp("fake_app_2", owner_settings_service_.get()); EXPECT_EQ("", manager()->GetAutoLaunchApp()); - manager()->RemoveApp("fake_app_2"); + manager()->RemoveApp("fake_app_2", owner_settings_service_.get()); EXPECT_EQ("fake_app_1", GetAppIds()); // Set a none exist app as auto launch. - manager()->SetAutoLaunchApp("none_exist_app"); + manager()->SetAutoLaunchApp("none_exist_app", owner_settings_service_.get()); EXPECT_EQ("", manager()->GetAutoLaunchApp()); EXPECT_FALSE(manager()->IsAutoLaunchEnabled()); // Add an existing app again. - manager()->AddApp("fake_app_1"); + manager()->AddApp("fake_app_1", owner_settings_service_.get()); EXPECT_EQ("fake_app_1", GetAppIds()); } @@ -473,7 +485,7 @@ IN_PROC_BROWSER_TEST_F(KioskAppManagerTest, BadApp) { AppDataLoadWaiter waiter(manager(), 2); - manager()->AddApp("unknown_app"); + manager()->AddApp("unknown_app", owner_settings_service_.get()); waiter.Wait(); EXPECT_FALSE(waiter.loaded()); EXPECT_EQ("", GetAppIds()); @@ -484,7 +496,7 @@ // chrome/test/data/chromeos/app_mode/webstore/inlineinstall/detail/app_1 fake_cws()->SetNoUpdate("app_1"); AppDataLoadWaiter waiter(manager(), 2); - manager()->AddApp("app_1"); + manager()->AddApp("app_1", owner_settings_service_.get()); waiter.Wait(); EXPECT_TRUE(waiter.loaded()); @@ -533,7 +545,7 @@ EXPECT_EQ("1.0.0", version); // Remove the app now. - manager()->RemoveApp(kTestLocalFsKioskApp); + manager()->RemoveApp(kTestLocalFsKioskApp, owner_settings_service_.get()); content::RunAllBlockingPoolTasksUntilIdle(); manager()->GetApps(&apps); ASSERT_EQ(0u, apps.size()); @@ -615,7 +627,7 @@ EXPECT_TRUE(base::PathExists(v2_crx_path)); // Remove the app now. - manager()->RemoveApp(kTestLocalFsKioskApp); + manager()->RemoveApp(kTestLocalFsKioskApp, owner_settings_service_.get()); content::RunAllBlockingPoolTasksUntilIdle(); manager()->GetApps(&apps); ASSERT_EQ(0u, apps.size());
diff --git a/chrome/browser/chromeos/attestation/attestation_policy_observer.cc b/chrome/browser/chromeos/attestation/attestation_policy_observer.cc index ee8d5de3..138d8b0 100644 --- a/chrome/browser/chromeos/attestation/attestation_policy_observer.cc +++ b/chrome/browser/chromeos/attestation/attestation_policy_observer.cc
@@ -99,7 +99,7 @@ num_retries_(0), retry_delay_(kRetryDelay), weak_factory_(this) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); attestation_subscription_ = cros_settings_->AddSettingsObserver( kDeviceAttestationEnabled, base::Bind(&AttestationPolicyObserver::AttestationSettingChanged, @@ -118,7 +118,7 @@ num_retries_(0), retry_delay_(kRetryDelay), weak_factory_(this) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); attestation_subscription_ = cros_settings_->AddSettingsObserver( kDeviceAttestationEnabled, base::Bind(&AttestationPolicyObserver::AttestationSettingChanged, @@ -127,7 +127,7 @@ } AttestationPolicyObserver::~AttestationPolicyObserver() { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); } void AttestationPolicyObserver::AttestationSettingChanged() {
diff --git a/chrome/browser/chromeos/attestation/attestation_policy_observer_unittest.cc b/chrome/browser/chromeos/attestation/attestation_policy_observer_unittest.cc index 61fa81f..ebb0fd0 100644 --- a/chrome/browser/chromeos/attestation/attestation_policy_observer_unittest.cc +++ b/chrome/browser/chromeos/attestation/attestation_policy_observer_unittest.cc
@@ -10,9 +10,7 @@ #include "chrome/browser/chromeos/attestation/attestation_key_payload.pb.h" #include "chrome/browser/chromeos/attestation/attestation_policy_observer.h" #include "chrome/browser/chromeos/attestation/fake_certificate.h" -#include "chrome/browser/chromeos/settings/cros_settings.h" -#include "chrome/browser/chromeos/settings/device_settings_service.h" -#include "chrome/browser/chromeos/settings/stub_cros_settings_provider.h" +#include "chrome/browser/chromeos/settings/scoped_cros_settings_test_helper.h" #include "chromeos/attestation/mock_attestation_flow.h" #include "chromeos/dbus/mock_cryptohome_client.h" #include "chromeos/settings/cros_settings_names.h" @@ -80,23 +78,11 @@ public: AttestationPolicyObserverTest() : ui_thread_(content::BrowserThread::UI, &message_loop_) { - // Remove the real DeviceSettingsProvider and replace it with a stub. - CrosSettings* cros_settings = CrosSettings::Get(); - device_settings_provider_ = - cros_settings->GetProvider(kDeviceAttestationEnabled); - cros_settings->RemoveSettingsProvider(device_settings_provider_); - cros_settings->AddSettingsProvider(&stub_settings_provider_); - cros_settings->SetBoolean(kDeviceAttestationEnabled, true); + settings_helper_.ReplaceProvider(kDeviceAttestationEnabled); + settings_helper_.SetBoolean(kDeviceAttestationEnabled, true); policy_client_.SetDMToken("fake_dm_token"); } - virtual ~AttestationPolicyObserverTest() { - // Restore the real DeviceSettingsProvider. - CrosSettings* cros_settings = CrosSettings::Get(); - cros_settings->RemoveSettingsProvider(&stub_settings_provider_); - cros_settings->AddSettingsProvider(device_settings_provider_); - } - protected: enum MockOptions { MOCK_KEY_EXISTS = 1, // Configure so a certified key exists. @@ -168,18 +154,14 @@ base::MessageLoopForUI message_loop_; content::TestBrowserThread ui_thread_; - ScopedTestDeviceSettingsService test_device_settings_service_; - ScopedTestCrosSettings test_cros_settings_; - CrosSettingsProvider* device_settings_provider_; - StubCrosSettingsProvider stub_settings_provider_; + ScopedCrosSettingsTestHelper settings_helper_; StrictMock<MockCryptohomeClient> cryptohome_client_; StrictMock<MockAttestationFlow> attestation_flow_; StrictMock<policy::MockCloudPolicyClient> policy_client_; }; TEST_F(AttestationPolicyObserverTest, FeatureDisabled) { - CrosSettings* cros_settings = CrosSettings::Get(); - cros_settings->SetBoolean(kDeviceAttestationEnabled, false); + settings_helper_.SetBoolean(kDeviceAttestationEnabled, false); Run(); }
diff --git a/chrome/browser/chromeos/attestation/platform_verification_flow.cc b/chrome/browser/chromeos/attestation/platform_verification_flow.cc index cb15208..03170f4 100644 --- a/chrome/browser/chromeos/attestation/platform_verification_flow.cc +++ b/chrome/browser/chromeos/attestation/platform_verification_flow.cc
@@ -141,7 +141,7 @@ cryptohome_client_(DBusThreadManager::Get()->GetCryptohomeClient()), delegate_(NULL), timeout_delay_(base::TimeDelta::FromSeconds(kTimeoutInSeconds)) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); scoped_ptr<ServerProxy> attestation_ca_client(new AttestationCAClient()); default_attestation_flow_.reset(new AttestationFlow( async_caller_, @@ -162,7 +162,7 @@ cryptohome_client_(cryptohome_client), delegate_(delegate), timeout_delay_(base::TimeDelta::FromSeconds(kTimeoutInSeconds)) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); if (!delegate_) { default_delegate_.reset(new DefaultDelegate()); delegate_ = default_delegate_.get(); @@ -177,7 +177,7 @@ const std::string& service_id, const std::string& challenge, const ChallengeCallback& callback) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); if (!delegate_->GetURL(web_contents).is_valid()) { LOG(WARNING) << "PlatformVerificationFlow: Invalid URL.";
diff --git a/chrome/browser/chromeos/attestation/platform_verification_flow_unittest.cc b/chrome/browser/chromeos/attestation/platform_verification_flow_unittest.cc index a3101e5..5319ddf 100644 --- a/chrome/browser/chromeos/attestation/platform_verification_flow_unittest.cc +++ b/chrome/browser/chromeos/attestation/platform_verification_flow_unittest.cc
@@ -11,9 +11,7 @@ #include "chrome/browser/chromeos/attestation/fake_certificate.h" #include "chrome/browser/chromeos/attestation/platform_verification_flow.h" #include "chrome/browser/chromeos/login/users/mock_user_manager.h" -#include "chrome/browser/chromeos/settings/cros_settings.h" -#include "chrome/browser/chromeos/settings/device_settings_service.h" -#include "chrome/browser/chromeos/settings/stub_cros_settings_provider.h" +#include "chrome/browser/chromeos/settings/scoped_cros_settings_test_helper.h" #include "chrome/browser/profiles/profile_impl.h" #include "chrome/common/pref_names.h" #include "chromeos/attestation/mock_attestation_flow.h" @@ -153,20 +151,8 @@ callback_ = base::Bind(&PlatformVerificationFlowTest::FakeChallengeCallback, base::Unretained(this)); - // Configure the global cros_settings. - CrosSettings* cros_settings = CrosSettings::Get(); - device_settings_provider_ = - cros_settings->GetProvider(kAttestationForContentProtectionEnabled); - cros_settings->RemoveSettingsProvider(device_settings_provider_); - cros_settings->AddSettingsProvider(&stub_settings_provider_); - cros_settings->SetBoolean(kAttestationForContentProtectionEnabled, true); - } - - void TearDown() { - // Restore the real DeviceSettingsProvider. - CrosSettings* cros_settings = CrosSettings::Get(); - cros_settings->RemoveSettingsProvider(&stub_settings_provider_); - cros_settings->AddSettingsProvider(device_settings_provider_); + settings_helper_.ReplaceProvider(kAttestationForContentProtectionEnabled); + settings_helper_.SetBoolean(kAttestationForContentProtectionEnabled, true); } void ExpectAttestationFlow() { @@ -239,10 +225,7 @@ cryptohome::MockAsyncMethodCaller mock_async_caller_; CustomFakeCryptohomeClient fake_cryptohome_client_; FakeDelegate fake_delegate_; - CrosSettingsProvider* device_settings_provider_; - StubCrosSettingsProvider stub_settings_provider_; - ScopedTestDeviceSettingsService test_device_settings_service_; - ScopedTestCrosSettings test_cros_settings_; + ScopedCrosSettingsTestHelper settings_helper_; scoped_refptr<PlatformVerificationFlow> verifier_; // Controls result of FakeGetCertificate. @@ -279,8 +262,7 @@ } TEST_F(PlatformVerificationFlowTest, FeatureDisabledByPolicy) { - CrosSettings::Get()->SetBoolean(kAttestationForContentProtectionEnabled, - false); + settings_helper_.SetBoolean(kAttestationForContentProtectionEnabled, false); verifier_->ChallengePlatformKey(NULL, kTestID, kTestChallenge, callback_); base::RunLoop().RunUntilIdle(); EXPECT_EQ(PlatformVerificationFlow::POLICY_REJECTED, result_);
diff --git a/chrome/browser/chromeos/base/locale_util.cc b/chrome/browser/chromeos/base/locale_util.cc index c185d78d..37e659b 100644 --- a/chrome/browser/chromeos/base/locale_util.cc +++ b/chrome/browser/chromeos/base/locale_util.cc
@@ -50,7 +50,7 @@ // Callback after SwitchLanguageDoReloadLocale() back in UI thread. void FinishSwitchLanguage(scoped_ptr<SwitchLanguageData> data) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); if (data->result.success) { g_browser_process->SetApplicationLocale(data->result.loaded_locale); @@ -107,7 +107,7 @@ const bool enable_locale_keyboard_layouts, const bool login_layouts_only, const SwitchLanguageCallback& callback) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); scoped_ptr<SwitchLanguageData> data(new SwitchLanguageData( locale, enable_locale_keyboard_layouts, login_layouts_only, callback)); base::Closure reloader(
diff --git a/chrome/browser/chromeos/bluetooth/bluetooth_pairing_dialog.cc b/chrome/browser/chromeos/bluetooth/bluetooth_pairing_dialog.cc index 4943dd1e..ff089f38 100644 --- a/chrome/browser/chromeos/bluetooth/bluetooth_pairing_dialog.cc +++ b/chrome/browser/chromeos/bluetooth/bluetooth_pairing_dialog.cc
@@ -86,8 +86,7 @@ void BluetoothPairingDialog::OnCloseContents(WebContents* source, bool* out_close_dialog) { - if (out_close_dialog) - *out_close_dialog = true; + *out_close_dialog = true; } bool BluetoothPairingDialog::ShouldShowDialogTitle() const {
diff --git a/chrome/browser/chromeos/customization/customization_wallpaper_downloader.cc b/chrome/browser/chromeos/customization/customization_wallpaper_downloader.cc index 8e09a5a..3213831d 100644 --- a/chrome/browser/chromeos/customization/customization_wallpaper_downloader.cc +++ b/chrome/browser/chromeos/customization/customization_wallpaper_downloader.cc
@@ -75,15 +75,15 @@ retry_delay_(base::TimeDelta::FromSeconds(kRetrySleepSeconds)), on_wallpaper_fetch_completed_(on_wallpaper_fetch_completed), weak_factory_(this) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); } CustomizationWallpaperDownloader::~CustomizationWallpaperDownloader() { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); } void CustomizationWallpaperDownloader::StartRequest() { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); DCHECK(wallpaper_url_.is_valid()); url_fetcher_.reset( @@ -103,7 +103,7 @@ } void CustomizationWallpaperDownloader::Retry() { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); ++retries_; const double delay_seconds = std::min( @@ -119,7 +119,7 @@ } void CustomizationWallpaperDownloader::Start() { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); scoped_ptr<bool> success(new bool(false)); base::Closure mkdir_closure = base::Bind(&CreateWallpaperDirectory, @@ -137,14 +137,14 @@ void CustomizationWallpaperDownloader::OnWallpaperDirectoryCreated( scoped_ptr<bool> success) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); if (*success) StartRequest(); } void CustomizationWallpaperDownloader::OnURLFetchComplete( const net::URLFetcher* source) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); DCHECK_EQ(url_fetcher_.get(), source); const net::URLRequestStatus status = source->GetStatus(); @@ -188,7 +188,7 @@ void CustomizationWallpaperDownloader::OnTemporaryFileRenamed( scoped_ptr<bool> success) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); on_wallpaper_fetch_completed_.Run(*success, wallpaper_url_); }
diff --git a/chrome/browser/chromeos/display/display_preferences.cc b/chrome/browser/chromeos/display/display_preferences.cc index a71342d..dca2e5a2 100644 --- a/chrome/browser/chromeos/display/display_preferences.cc +++ b/chrome/browser/chromeos/display/display_preferences.cc
@@ -348,16 +348,17 @@ } void StoreDisplayRotationPrefs(bool rotation_lock) { - ash::DisplayManager* display_manager = GetDisplayManager(); - if (!display_manager->HasInternalDisplay()) + if (!gfx::Display::HasInternalDisplay()) return; PrefService* local_state = g_browser_process->local_state(); DictionaryPrefUpdate update(local_state, prefs::kDisplayRotationLock); base::DictionaryValue* pref_data = update.Get(); pref_data->SetBoolean("lock", rotation_lock); - gfx::Display::Rotation rotation = display_manager-> - GetDisplayInfo(gfx::Display::InternalDisplayId()).rotation(); + gfx::Display::Rotation rotation = + GetDisplayManager() + ->GetDisplayInfo(gfx::Display::InternalDisplayId()) + .rotation(); pref_data->SetInteger("orientation", static_cast<int>(rotation)); }
diff --git a/chrome/browser/chromeos/drive/change_list_loader.cc b/chrome/browser/chromeos/drive/change_list_loader.cc index 081d9a7..143927a 100644 --- a/chrome/browser/chromeos/drive/change_list_loader.cc +++ b/chrome/browser/chromeos/drive/change_list_loader.cc
@@ -10,6 +10,7 @@ #include "base/callback_helpers.h" #include "base/metrics/histogram.h" #include "base/strings/string_number_conversions.h" +#include "base/synchronization/cancellation_flag.h" #include "base/time/time.h" #include "chrome/browser/chromeos/drive/change_list_loader_observer.h" #include "chrome/browser/chromeos/drive/change_list_processor.h" @@ -294,6 +295,7 @@ LoaderController* loader_controller) : logger_(logger), blocking_task_runner_(blocking_task_runner), + in_shutdown_(new base::CancellationFlag), resource_metadata_(resource_metadata), scheduler_(scheduler), about_resource_loader_(about_resource_loader), @@ -303,6 +305,10 @@ } ChangeListLoader::~ChangeListLoader() { + in_shutdown_->Set(); + // Delete |in_shutdown_| with the blocking task runner so that it gets deleted + // after all active ChangeListProcessors. + blocking_task_runner_->DeleteSoon(FROM_HERE, in_shutdown_.release()); } bool ChangeListLoader::IsRefreshing() const { @@ -526,7 +532,7 @@ } ChangeListProcessor* change_list_processor = - new ChangeListProcessor(resource_metadata_); + new ChangeListProcessor(resource_metadata_, in_shutdown_.get()); // Don't send directory content change notification while performing // the initial content retrieval. const bool should_notify_changed_directories = is_delta_update;
diff --git a/chrome/browser/chromeos/drive/change_list_loader.h b/chrome/browser/chromeos/drive/change_list_loader.h index 3486aa48..7bdac52 100644 --- a/chrome/browser/chromeos/drive/change_list_loader.h +++ b/chrome/browser/chromeos/drive/change_list_loader.h
@@ -20,6 +20,7 @@ class GURL; namespace base { +class CancellationFlag; class ScopedClosureRunner; class SequencedTaskRunner; class Time; @@ -213,6 +214,7 @@ EventLogger* logger_; // Not owned. scoped_refptr<base::SequencedTaskRunner> blocking_task_runner_; + scoped_ptr<base::CancellationFlag> in_shutdown_; ResourceMetadata* resource_metadata_; // Not owned. JobScheduler* scheduler_; // Not owned. AboutResourceLoader* about_resource_loader_; // Not owned.
diff --git a/chrome/browser/chromeos/drive/change_list_processor.cc b/chrome/browser/chromeos/drive/change_list_processor.cc index fb4f46e..4f1b4ce 100644 --- a/chrome/browser/chromeos/drive/change_list_processor.cc +++ b/chrome/browser/chromeos/drive/change_list_processor.cc
@@ -6,6 +6,7 @@ #include "base/metrics/histogram.h" #include "base/strings/string_number_conversions.h" +#include "base/synchronization/cancellation_flag.h" #include "chrome/browser/chromeos/drive/drive.pb.h" #include "chrome/browser/chromeos/drive/file_change.h" #include "chrome/browser/chromeos/drive/file_system_util.h" @@ -113,8 +114,11 @@ ChangeList::~ChangeList() {} -ChangeListProcessor::ChangeListProcessor(ResourceMetadata* resource_metadata) - : resource_metadata_(resource_metadata), changed_files_(new FileChange) { +ChangeListProcessor::ChangeListProcessor(ResourceMetadata* resource_metadata, + base::CancellationFlag* in_shutdown) + : resource_metadata_(resource_metadata), + in_shutdown_(in_shutdown), + changed_files_(new FileChange) { } ChangeListProcessor::~ChangeListProcessor() { @@ -230,6 +234,9 @@ // Apply all entries except deleted ones to the metadata. std::vector<std::string> deleted_resource_ids; while (!entry_map_.empty()) { + if (in_shutdown_ && in_shutdown_->IsSet()) + return FILE_ERROR_ABORT; + ResourceEntryMap::iterator it = entry_map_.begin(); // Process deleted entries later to avoid deleting moved entries under it.
diff --git a/chrome/browser/chromeos/drive/change_list_processor.h b/chrome/browser/chromeos/drive/change_list_processor.h index 61ecf65..83a056e 100644 --- a/chrome/browser/chromeos/drive/change_list_processor.h +++ b/chrome/browser/chromeos/drive/change_list_processor.h
@@ -16,11 +16,15 @@ #include "chrome/browser/chromeos/drive/file_errors.h" #include "url/gurl.h" +namespace base { +class CancellationFlag; +} // namespace base + namespace google_apis { class AboutResource; class ChangeList; class FileList; -} // google_apis +} // namespace google_apis namespace drive { @@ -103,7 +107,8 @@ // updates the resource metadata stored locally. class ChangeListProcessor { public: - explicit ChangeListProcessor(ResourceMetadata* resource_metadata); + ChangeListProcessor(ResourceMetadata* resource_metadata, + base::CancellationFlag* in_shutdown); ~ChangeListProcessor(); // Applies change lists or full resource lists to |resource_metadata_|. @@ -151,6 +156,7 @@ void UpdateChangedDirs(const ResourceEntry& entry); ResourceMetadata* resource_metadata_; // Not owned. + base::CancellationFlag* in_shutdown_; // Not owned. ResourceEntryMap entry_map_; ParentResourceIdMap parent_resource_id_map_;
diff --git a/chrome/browser/chromeos/drive/change_list_processor_unittest.cc b/chrome/browser/chromeos/drive/change_list_processor_unittest.cc index be3457c..5ba073e 100644 --- a/chrome/browser/chromeos/drive/change_list_processor_unittest.cc +++ b/chrome/browser/chromeos/drive/change_list_processor_unittest.cc
@@ -126,7 +126,7 @@ about_resource->set_largest_change_id(kBaseResourceListChangestamp); about_resource->set_root_folder_id(kRootId); - ChangeListProcessor processor(metadata_.get()); + ChangeListProcessor processor(metadata_.get(), nullptr); return processor.Apply(about_resource.Pass(), changes.Pass(), false /* is_delta_update */); @@ -141,7 +141,7 @@ about_resource->set_largest_change_id(kBaseResourceListChangestamp); about_resource->set_root_folder_id(kRootId); - ChangeListProcessor processor(metadata_.get()); + ChangeListProcessor processor(metadata_.get(), nullptr); FileError error = processor.Apply(about_resource.Pass(), changes.Pass(), true /* is_delta_update */);
diff --git a/chrome/browser/chromeos/drive/file_task_executor.cc b/chrome/browser/chromeos/drive/file_task_executor.cc index b91fc3a..eb799a2 100644 --- a/chrome/browser/chromeos/drive/file_task_executor.cc +++ b/chrome/browser/chromeos/drive/file_task_executor.cc
@@ -136,7 +136,7 @@ void FileTaskExecutor::OnAppAuthorized(const std::string& resource_id, google_apis::DriveApiErrorCode error, const GURL& open_link) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); if (error != google_apis::HTTP_SUCCESS || open_link.is_empty()) { Done(false); @@ -153,7 +153,7 @@ } void FileTaskExecutor::Done(bool success) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); if (!done_.is_null()) done_.Run(success ? extensions::api::file_manager_private::TASK_RESULT_OPENED
diff --git a/chrome/browser/chromeos/extensions/wallpaper_function_base.cc b/chrome/browser/chromeos/extensions/wallpaper_function_base.cc index 3331b7b..235f7fb 100644 --- a/chrome/browser/chromeos/extensions/wallpaper_function_base.cc +++ b/chrome/browser/chromeos/extensions/wallpaper_function_base.cc
@@ -45,9 +45,7 @@ : public ImageDecoder::ImageRequest { public: explicit UnsafeWallpaperDecoder(scoped_refptr<WallpaperFunctionBase> function) - : ImageRequest( - BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI)), - function_(function) {} + : function_(function) {} void Start(const std::vector<char>& image_data) { DCHECK_CURRENTLY_ON(BrowserThread::UI);
diff --git a/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc b/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc index cef9f59..8c9d28a 100644 --- a/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc +++ b/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
@@ -1040,14 +1040,10 @@ TestParameter(NOT_IN_GUEST_MODE, "traverseDrive"))); // Slow tests are disabled on debug build. http://crbug.com/327719 -// Disabled under MSAN as well. http://crbug.com/468980. -#if !defined(NDEBUG) || defined(MEMORY_SANITIZER) -#define MAYBE_SuggestAppDialog DISABLED_SuggestAppDialog -#else -#define MAYBE_SuggestAppDialog SuggestAppDialog -#endif +// Disabled under MSAN, ASAN, and LSAN as well. http://crbug.com/479757. +// Flakes often: http://crbug.com/479757 WRAPPED_INSTANTIATE_TEST_CASE_P( - MAYBE_SuggestAppDialog, + DISABLED_SuggestAppDialog, FileManagerBrowserTest, ::testing::Values(TestParameter(NOT_IN_GUEST_MODE, "suggestAppDialog"))); @@ -1153,8 +1149,9 @@ "tabindexFocusDownloads"), TestParameter(IN_GUEST_MODE, "tabindexFocusDownloads"))); +// http://crbug.com/469061 INSTANTIATE_TEST_CASE_P( - TabindexFocusDirectorySelected, + DISABLED_TabindexFocusDirectorySelected, FileManagerBrowserTest, ::testing::Values(TestParameter(NOT_IN_GUEST_MODE, "tabindexFocusDirectorySelected")));
diff --git a/chrome/browser/chromeos/first_run/drive_first_run_controller.cc b/chrome/browser/chromeos/first_run/drive_first_run_controller.cc index 63aedbb8..89ddecc 100644 --- a/chrome/browser/chromeos/first_run/drive_first_run_controller.cc +++ b/chrome/browser/chromeos/first_run/drive_first_run_controller.cc
@@ -437,7 +437,7 @@ } void DriveFirstRunController::OnOfflineInit(bool success, UMAOutcome outcome) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); if (success) ShowNotification(); UMA_HISTOGRAM_ENUMERATION("DriveOffline.CrosAutoEnableOutcome",
diff --git a/chrome/browser/chromeos/input_method/input_method_engine.cc b/chrome/browser/chromeos/input_method/input_method_engine.cc index 4fcbd03d..6d5ad0ca 100644 --- a/chrome/browser/chromeos/input_method/input_method_engine.cc +++ b/chrome/browser/chromeos/input_method/input_method_engine.cc
@@ -596,6 +596,10 @@ observer_->OnReset(active_component_id_); } +bool InputMethodEngine::IsInterestedInKeyEvent() const { + return observer_->IsInterestedInKeyEvent(); +} + void InputMethodEngine::ProcessKeyEvent( const ui::KeyEvent& key_event, const KeyEventDoneCallback& callback) {
diff --git a/chrome/browser/chromeos/input_method/input_method_engine.h b/chrome/browser/chromeos/input_method/input_method_engine.h index 36fc38d..d475a439 100644 --- a/chrome/browser/chromeos/input_method/input_method_engine.h +++ b/chrome/browser/chromeos/input_method/input_method_engine.h
@@ -93,6 +93,8 @@ int GetCotextIdForTesting() { return context_id_; } + bool IsInterestedInKeyEvent() const override; + private: // Converts MenuItem to InputMethodMenuItem. void MenuItemToProperty(const MenuItem& item,
diff --git a/chrome/browser/chromeos/input_method/input_method_engine_interface.h b/chrome/browser/chromeos/input_method/input_method_engine_interface.h index 80f73a2..b183a3d4 100644 --- a/chrome/browser/chromeos/input_method/input_method_engine_interface.h +++ b/chrome/browser/chromeos/input_method/input_method_engine_interface.h
@@ -150,6 +150,9 @@ // Called when an InputContext's properties change while it is focused. virtual void OnInputContextUpdate(const InputContext& context) = 0; + // Returns whether the observer is interested in key events. + virtual bool IsInterestedInKeyEvent() const = 0; + // Called when the user pressed a key with a text field focused. virtual void OnKeyEvent(const std::string& engine_id, const KeyboardEvent& event,
diff --git a/chrome/browser/chromeos/input_method/input_method_engine_unittest.cc b/chrome/browser/chromeos/input_method/input_method_engine_unittest.cc index d38f27f..b31c17f 100644 --- a/chrome/browser/chromeos/input_method/input_method_engine_unittest.cc +++ b/chrome/browser/chromeos/input_method/input_method_engine_unittest.cc
@@ -79,6 +79,7 @@ calls_bitmap_ |= ONFOCUS; } void OnBlur(int context_id) override { calls_bitmap_ |= ONBLUR; } + bool IsInterestedInKeyEvent() const override { return true; } void OnKeyEvent(const std::string& engine_id, const InputMethodEngineInterface::KeyboardEvent& event, input_method::KeyEventHandle* key_data) override {}
diff --git a/chrome/browser/chromeos/input_method/mock_input_method_engine.cc b/chrome/browser/chromeos/input_method/mock_input_method_engine.cc index 2686cfc..0094309 100644 --- a/chrome/browser/chromeos/input_method/mock_input_method_engine.cc +++ b/chrome/browser/chromeos/input_method/mock_input_method_engine.cc
@@ -116,6 +116,10 @@ void MockInputMethodEngine::Reset() { } +bool MockInputMethodEngine::IsInterestedInKeyEvent() const { + return true; +} + void MockInputMethodEngine::ProcessKeyEvent( const ui::KeyEvent& key_event, const KeyEventDoneCallback& callback) {
diff --git a/chrome/browser/chromeos/input_method/mock_input_method_engine.h b/chrome/browser/chromeos/input_method/mock_input_method_engine.h index e722811..3a605750 100644 --- a/chrome/browser/chromeos/input_method/mock_input_method_engine.h +++ b/chrome/browser/chromeos/input_method/mock_input_method_engine.h
@@ -74,6 +74,7 @@ void Disable() override; void PropertyActivate(const std::string& property_name) override; void Reset() override; + bool IsInterestedInKeyEvent() const override; void ProcessKeyEvent(const ui::KeyEvent& key_event, const KeyEventDoneCallback& callback) override; void CandidateClicked(uint32 index) override;
diff --git a/chrome/browser/chromeos/login/auth/cryptohome_authenticator_unittest.cc b/chrome/browser/chromeos/login/auth/cryptohome_authenticator_unittest.cc index c3dcd6e..2607ace 100644 --- a/chrome/browser/chromeos/login/auth/cryptohome_authenticator_unittest.cc +++ b/chrome/browser/chromeos/login/auth/cryptohome_authenticator_unittest.cc
@@ -22,7 +22,7 @@ #include "chrome/browser/chromeos/profiles/profile_helper.h" #include "chrome/browser/chromeos/settings/cros_settings.h" #include "chrome/browser/chromeos/settings/device_settings_test_helper.h" -#include "chrome/browser/chromeos/settings/stub_cros_settings_provider.h" +#include "chrome/browser/chromeos/settings/scoped_cros_settings_test_helper.h" #include "chrome/test/base/testing_browser_process.h" #include "chrome/test/base/testing_profile.h" #include "chrome/test/base/testing_profile_manager.h" @@ -434,15 +434,9 @@ // and succeeded but we are in safe mode and the current user is not owner. state_->PresetCryptohomeStatus(true, cryptohome::MOUNT_ERROR_NONE); SetOwnerState(false, false); - // Remove the real DeviceSettingsProvider and replace it with a stub. - CrosSettingsProvider* device_settings_provider = - CrosSettings::Get()->GetProvider(chromeos::kReportDeviceVersionInfo); - EXPECT_TRUE(device_settings_provider != NULL); - EXPECT_TRUE( - CrosSettings::Get()->RemoveSettingsProvider(device_settings_provider)); - StubCrosSettingsProvider stub_settings_provider; - CrosSettings::Get()->AddSettingsProvider(&stub_settings_provider); - CrosSettings::Get()->SetBoolean(kPolicyMissingMitigationMode, true); + ScopedCrosSettingsTestHelper settings_helper(false); + settings_helper.ReplaceProvider(kPolicyMissingMitigationMode); + settings_helper.SetBoolean(kPolicyMissingMitigationMode, true); // Initialize login state for this test to verify the login state is changed // to SAFE_MODE. @@ -468,9 +462,6 @@ // Unset global objects used by this test. fake_cryptohome_client_->set_unmount_result(true); LoginState::Shutdown(); - EXPECT_TRUE( - CrosSettings::Get()->RemoveSettingsProvider(&stub_settings_provider)); - CrosSettings::Get()->AddSettingsProvider(device_settings_provider); } // Test the case that login switches to SafeMode and the Owner logs in, which @@ -493,15 +484,9 @@ // and succeeded but we are in safe mode and the current user is not owner. state_->PresetCryptohomeStatus(true, cryptohome::MOUNT_ERROR_NONE); SetOwnerState(false, false); - // Remove the real DeviceSettingsProvider and replace it with a stub. - CrosSettingsProvider* device_settings_provider = - CrosSettings::Get()->GetProvider(chromeos::kReportDeviceVersionInfo); - EXPECT_TRUE(device_settings_provider != NULL); - EXPECT_TRUE( - CrosSettings::Get()->RemoveSettingsProvider(device_settings_provider)); - StubCrosSettingsProvider stub_settings_provider; - CrosSettings::Get()->AddSettingsProvider(&stub_settings_provider); - CrosSettings::Get()->SetBoolean(kPolicyMissingMitigationMode, true); + ScopedCrosSettingsTestHelper settings_helper(false); + settings_helper.ReplaceProvider(kPolicyMissingMitigationMode); + settings_helper.SetBoolean(kPolicyMissingMitigationMode, true); // Initialize login state for this test to verify the login state is changed // to SAFE_MODE. @@ -526,9 +511,6 @@ // Unset global objects used by this test. fake_cryptohome_client_->set_unmount_result(true); LoginState::Shutdown(); - EXPECT_TRUE( - CrosSettings::Get()->RemoveSettingsProvider(&stub_settings_provider)); - CrosSettings::Get()->AddSettingsProvider(device_settings_provider); } TEST_F(CryptohomeAuthenticatorTest, DriveFailedMount) {
diff --git a/chrome/browser/chromeos/login/easy_unlock/bootstrap_browsertest.cc b/chrome/browser/chromeos/login/easy_unlock/bootstrap_browsertest.cc index e41275f..e87829c 100644 --- a/chrome/browser/chromeos/login/easy_unlock/bootstrap_browsertest.cc +++ b/chrome/browser/chromeos/login/easy_unlock/bootstrap_browsertest.cc
@@ -57,8 +57,13 @@ } // namespace -class BootstrapTest : public OobeBaseTest { +// Boolean parameter is used to run this test for webview (true) and for +// iframe (false) GAIA sign in. +class BootstrapTest : public OobeBaseTest, + public testing::WithParamInterface<bool> { public: + BootstrapTest() { set_use_webview(GetParam()); } + void SetUpCommandLine(base::CommandLine* command_line) override { OobeBaseTest::SetUpCommandLine(command_line); @@ -129,7 +134,7 @@ } }; -IN_PROC_BROWSER_TEST_F(BootstrapTest, Basic) { +IN_PROC_BROWSER_TEST_P(BootstrapTest, Basic) { ScopedCompleteCallbackForTesting scoped_bootstrap_initialized(base::Bind( &BootstrapTest::OnBootstrapInitialized, base::Unretained(this))); @@ -142,7 +147,7 @@ content::NotificationService::AllSources()).Wait(); } -IN_PROC_BROWSER_TEST_F(BootstrapTest, PRE_CleanUpFailedUser) { +IN_PROC_BROWSER_TEST_P(BootstrapTest, PRE_CleanUpFailedUser) { ScopedCompleteCallbackForTesting scoped_bootstrap_initialized(base::Bind( &BootstrapTest::OnBootstrapInitialized, base::Unretained(this))); @@ -154,8 +159,13 @@ content::RunMessageLoop(); } -IN_PROC_BROWSER_TEST_F(BootstrapTest, CleanUpFailedUser) { +IN_PROC_BROWSER_TEST_P(BootstrapTest, CleanUpFailedUser) { EXPECT_FALSE(user_manager::UserManager::Get()->IsKnownUser(kFakeUser)); } +// TODO(nkostylev): Fix this test for webview. http://crbug.com/477402 +INSTANTIATE_TEST_CASE_P(BootstrapTestSuite, + BootstrapTest, + testing::Values(false)); + } // namespace chromeos
diff --git a/chrome/browser/chromeos/login/easy_unlock/easy_unlock_key_manager.cc b/chrome/browser/chromeos/login/easy_unlock/easy_unlock_key_manager.cc index a33cc520..c1ba482f 100644 --- a/chrome/browser/chromeos/login/easy_unlock/easy_unlock_key_manager.cc +++ b/chrome/browser/chromeos/login/easy_unlock/easy_unlock_key_manager.cc
@@ -59,7 +59,9 @@ return; } - if (tpm_key_manager->PrepareTpmKey(false /* check_private_key */, + // Private TPM key is needed only when adding new keys. + if (remote_devices.empty() || + tpm_key_manager->PrepareTpmKey(false /* check_private_key */, do_refresh_keys)) { do_refresh_keys.Run(); } else {
diff --git a/chrome/browser/chromeos/login/enrollment/enrollment_screen.cc b/chrome/browser/chromeos/login/enrollment/enrollment_screen.cc index 07039aed..8e6c4f4 100644 --- a/chrome/browser/chromeos/login/enrollment/enrollment_screen.cc +++ b/chrome/browser/chromeos/login/enrollment/enrollment_screen.cc
@@ -141,14 +141,7 @@ } } -void EnrollmentScreen::ConfigureHost(bool accepted_eula, - const std::string& lang, - const std::string& timezone, - bool send_reports, - const std::string& keyboard_layout) { -} - -void EnrollmentScreen::EnrollHost(const std::string& auth_token) { +void EnrollmentScreen::EnrollHostRequested(const std::string& auth_token) { actor_->Show(); actor_->ShowEnrollmentSpinnerScreen(); CreateEnrollmentHelper();
diff --git a/chrome/browser/chromeos/login/enrollment/enrollment_screen.h b/chrome/browser/chromeos/login/enrollment/enrollment_screen.h index 664cc63..b932144 100644 --- a/chrome/browser/chromeos/login/enrollment/enrollment_screen.h +++ b/chrome/browser/chromeos/login/enrollment/enrollment_screen.h
@@ -68,12 +68,7 @@ // pairing_chromeos::HostPairingController::Observer: void PairingStageChanged(Stage new_stage) override; - void ConfigureHost(bool accepted_eula, - const std::string& lang, - const std::string& timezone, - bool send_reports, - const std::string& keyboard_layout) override; - void EnrollHost(const std::string& auth_token) override; + void EnrollHostRequested(const std::string& auth_token) override; // EnrollmentScreenActor::Controller implementation: void OnLoginDone(const std::string& user,
diff --git a/chrome/browser/chromeos/login/helper.cc b/chrome/browser/chromeos/login/helper.cc index decb068..a0177c5d 100644 --- a/chrome/browser/chromeos/login/helper.cc +++ b/chrome/browser/chromeos/login/helper.cc
@@ -6,6 +6,7 @@ #include "base/bind.h" #include "base/command_line.h" +#include "base/json/json_reader.h" #include "base/strings/utf_string_conversions.h" #include "chrome/browser/chromeos/login/startup_utils.h" #include "chrome/browser/chromeos/login/ui/login_display_host_impl.h" @@ -13,6 +14,7 @@ #include "chrome/browser/chromeos/profiles/profile_helper.h" #include "chrome/grit/generated_resources.h" #include "chromeos/chromeos_switches.h" +#include "chromeos/network/managed_network_configuration_handler.h" #include "chromeos/network/network_handler.h" #include "chromeos/network/network_state.h" #include "chromeos/network/network_state_handler.h" @@ -131,18 +133,50 @@ return base::string16(); } +void NetworkStateHelper::CreateNetworkFromOnc( + const std::string& onc_spec) const { + std::string error; + scoped_ptr<base::Value> root(base::JSONReader::ReadAndReturnError( + onc_spec, base::JSON_ALLOW_TRAILING_COMMAS, nullptr, &error)); + + base::DictionaryValue* toplevel_onc = nullptr; + if (!root || !root->GetAsDictionary(&toplevel_onc)) { + LOG(ERROR) << "Invalid JSON Dictionary: " << error; + return; + } + + NetworkHandler::Get()->managed_network_configuration_handler()-> + CreateConfiguration( + "", *toplevel_onc, + base::Bind(&NetworkStateHelper::OnCreateConfiguration, + base::Unretained(this)), + base::Bind(&NetworkStateHelper::OnCreateConfigurationFailed, + base::Unretained(this))); +} + +void NetworkStateHelper::OnCreateConfiguration( + const std::string& service_path) const { + // Do Nothing. +} + +void NetworkStateHelper::OnCreateConfigurationFailed( + const std::string& error_name, + scoped_ptr<base::DictionaryValue> error_data) const { + LOG(ERROR) << "Failed to create network configuration: " << error_name; +} + bool NetworkStateHelper::IsConnected() const { chromeos::NetworkStateHandler* nsh = chromeos::NetworkHandler::Get()->network_state_handler(); return nsh->ConnectedNetworkByType(chromeos::NetworkTypePattern::Default()) != - NULL; + nullptr; } bool NetworkStateHelper::IsConnecting() const { chromeos::NetworkStateHandler* nsh = chromeos::NetworkHandler::Get()->network_state_handler(); return nsh->ConnectingNetworkByType( - chromeos::NetworkTypePattern::Default()) != NULL; + chromeos::NetworkTypePattern::Default()) != nullptr; } content::StoragePartition* GetSigninPartition() {
diff --git a/chrome/browser/chromeos/login/helper.h b/chrome/browser/chromeos/login/helper.h index 2809d66..c02cd5890 100644 --- a/chrome/browser/chromeos/login/helper.h +++ b/chrome/browser/chromeos/login/helper.h
@@ -64,6 +64,9 @@ // Ethernet > WiFi > Cellular. Same for connecting network. virtual base::string16 GetCurrentNetworkName() const; + // Add a network configuration. + virtual void CreateNetworkFromOnc(const std::string& onc_spec) const; + // Returns true if the default network is in connected state. virtual bool IsConnected() const; @@ -71,6 +74,11 @@ virtual bool IsConnecting() const; private: + void OnCreateConfiguration(const std::string& service_path) const; + void OnCreateConfigurationFailed( + const std::string& error_name, + scoped_ptr<base::DictionaryValue> error_data) const; + DISALLOW_COPY_AND_ASSIGN(NetworkStateHelper); };
diff --git a/chrome/browser/chromeos/login/kiosk_browsertest.cc b/chrome/browser/chromeos/login/kiosk_browsertest.cc index b4f1aa887..ad6d36b 100644 --- a/chrome/browser/chromeos/login/kiosk_browsertest.cc +++ b/chrome/browser/chromeos/login/kiosk_browsertest.cc
@@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include <vector> + #include "ash/desktop_background/desktop_background_controller.h" #include "ash/desktop_background/desktop_background_controller_observer.h" #include "ash/shell.h" @@ -14,11 +16,9 @@ #include "base/path_service.h" #include "base/prefs/pref_service.h" #include "base/run_loop.h" -#include "base/single_thread_task_runner.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" #include "base/synchronization/lock.h" -#include "base/thread_task_runner_handle.h" #include "chrome/browser/chrome_notification_types.h" #include "chrome/browser/chromeos/app_mode/fake_cws.h" #include "chrome/browser/chromeos/app_mode/kiosk_app_launch_error.h" @@ -35,13 +35,13 @@ #include "chrome/browser/chromeos/login/users/mock_user_manager.h" #include "chrome/browser/chromeos/login/users/scoped_user_manager_enabler.h" #include "chrome/browser/chromeos/login/wizard_controller.h" +#include "chrome/browser/chromeos/ownership/fake_owner_settings_service.h" +#include "chrome/browser/chromeos/policy/device_local_account.h" #include "chrome/browser/chromeos/policy/device_policy_cros_browser_test.h" -#include "chrome/browser/chromeos/policy/proto/chrome_device_policy.pb.h" #include "chrome/browser/chromeos/profiles/profile_helper.h" -#include "chrome/browser/chromeos/settings/cros_settings.h" #include "chrome/browser/chromeos/settings/device_oauth2_token_service.h" #include "chrome/browser/chromeos/settings/device_oauth2_token_service_factory.h" -#include "chrome/browser/chromeos/settings/device_settings_service.h" +#include "chrome/browser/chromeos/settings/scoped_cros_settings_test_helper.h" #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/profiles/profile_impl.h" #include "chrome/browser/profiles/profile_manager.h" @@ -410,74 +410,18 @@ DISALLOW_COPY_AND_ASSIGN(AppDataLoadWaiter); }; -class CrosSettingsPermanentlyUntrustedMaker : - public DeviceSettingsService::Observer { - public: - CrosSettingsPermanentlyUntrustedMaker(); - - // DeviceSettingsService::Observer: - void OwnershipStatusChanged() override; - void DeviceSettingsUpdated() override; - void OnDeviceSettingsServiceShutdown() override; - - private: - bool untrusted_check_running_; - base::RunLoop run_loop_; - - void CheckIfUntrusted(); - - DISALLOW_COPY_AND_ASSIGN(CrosSettingsPermanentlyUntrustedMaker); -}; - -CrosSettingsPermanentlyUntrustedMaker::CrosSettingsPermanentlyUntrustedMaker() - : untrusted_check_running_(false) { - DeviceSettingsService::Get()->AddObserver(this); - - policy::DevicePolicyCrosTestHelper().InstallOwnerKey(); - DeviceSettingsService::Get()->OwnerKeySet(true); - - run_loop_.Run(); -} - -void CrosSettingsPermanentlyUntrustedMaker::OwnershipStatusChanged() { - if (untrusted_check_running_) - return; - - base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, - base::Bind(&CrosSettingsPermanentlyUntrustedMaker::CheckIfUntrusted, - base::Unretained(this))); -} - -void CrosSettingsPermanentlyUntrustedMaker::DeviceSettingsUpdated() { -} - -void CrosSettingsPermanentlyUntrustedMaker::OnDeviceSettingsServiceShutdown() { -} - -void CrosSettingsPermanentlyUntrustedMaker::CheckIfUntrusted() { - untrusted_check_running_ = true; - const CrosSettingsProvider::TrustedStatus trusted_status = - CrosSettings::Get()->PrepareTrustedValues( - base::Bind(&CrosSettingsPermanentlyUntrustedMaker::CheckIfUntrusted, - base::Unretained(this))); - if (trusted_status == CrosSettingsProvider::TEMPORARILY_UNTRUSTED) - return; - untrusted_check_running_ = false; - - if (trusted_status == CrosSettingsProvider::TRUSTED) - return; - - DeviceSettingsService::Get()->RemoveObserver(this); - run_loop_.Quit(); -} - } // namespace -class KioskTest : public OobeBaseTest { +// Boolean parameter is used to run this test for webview (true) and for +// iframe (false) GAIA sign in. +class KioskTest : public OobeBaseTest, + public testing::WithParamInterface<bool> { public: - KioskTest() : use_consumer_kiosk_mode_(true), - fake_cws_(new FakeCWS) { + KioskTest() + : settings_helper_(false), + use_consumer_kiosk_mode_(true), + fake_cws_(new FakeCWS) { + set_use_webview(GetParam()); set_exit_when_last_browser_closes(false); } @@ -507,9 +451,13 @@ // Needed to avoid showing Gaia screen instead of owner signin for // consumer network down test cases. StartupUtils::MarkDeviceRegistered(base::Closure()); + settings_helper_.ReplaceProvider(kAccountsPrefDeviceLocalAccounts); + owner_settings_service_ = settings_helper_.CreateOwnerSettingsService( + ProfileManager::GetPrimaryUserProfile()); } void TearDownOnMainThread() override { + settings_helper_.RestoreProvider(); AppLaunchController::SetNetworkTimeoutCallbackForTesting(NULL); AppLaunchSigninScreen::SetUserManagerForTesting(NULL); @@ -537,8 +485,9 @@ SetupTestAppUpdateCheck(); // Remove then add to ensure NOTIFICATION_KIOSK_APPS_LOADED fires. - KioskAppManager::Get()->RemoveApp(test_app_id_); - KioskAppManager::Get()->AddApp(test_app_id_); + KioskAppManager::Get()->RemoveApp(test_app_id_, + owner_settings_service_.get()); + KioskAppManager::Get()->AddApp(test_app_id_, owner_settings_service_.get()); } void FireKioskAppSettingsChanged() { @@ -555,8 +504,9 @@ void ReloadAutolaunchKioskApps() { SetupTestAppUpdateCheck(); - KioskAppManager::Get()->AddApp(test_app_id_); - KioskAppManager::Get()->SetAutoLaunchApp(test_app_id_); + KioskAppManager::Get()->AddApp(test_app_id_, owner_settings_service_.get()); + KioskAppManager::Get()->SetAutoLaunchApp(test_app_id_, + owner_settings_service_.get()); } void StartUIForAppLaunch() { @@ -800,6 +750,9 @@ use_consumer_kiosk_mode_ = use; } + ScopedCrosSettingsTestHelper settings_helper_; + scoped_ptr<FakeOwnerSettingsService> owner_settings_service_; + private: bool use_consumer_kiosk_mode_; std::string test_app_id_; @@ -811,7 +764,7 @@ DISALLOW_COPY_AND_ASSIGN(KioskTest); }; -IN_PROC_BROWSER_TEST_F(KioskTest, InstallAndLaunchApp) { +IN_PROC_BROWSER_TEST_P(KioskTest, InstallAndLaunchApp) { StartAppLaunchFromLoginScreen(SimulateNetworkOnlineClosure()); WaitForAppLaunchSuccess(); KioskAppManager::App app; @@ -819,7 +772,7 @@ EXPECT_FALSE(app.was_auto_launched_with_zero_delay); } -IN_PROC_BROWSER_TEST_F(KioskTest, ZoomSupport) { +IN_PROC_BROWSER_TEST_P(KioskTest, ZoomSupport) { ExtensionTestMessageListener app_window_loaded_listener("appWindowLoaded", false); StartAppLaunchFromLoginScreen(SimulateNetworkOnlineClosure()); @@ -886,7 +839,7 @@ content::RunAllPendingInMessageLoop(); } -IN_PROC_BROWSER_TEST_F(KioskTest, NotSignedInWithGAIAAccount) { +IN_PROC_BROWSER_TEST_P(KioskTest, NotSignedInWithGAIAAccount) { // Tests that the kiosk session is not considered to be logged in with a GAIA // account. StartAppLaunchFromLoginScreen(SimulateNetworkOnlineClosure()); @@ -898,18 +851,18 @@ SigninManagerFactory::GetForProfile(app_profile)->IsAuthenticated()); } -IN_PROC_BROWSER_TEST_F(KioskTest, PRE_LaunchAppNetworkDown) { +IN_PROC_BROWSER_TEST_P(KioskTest, PRE_LaunchAppNetworkDown) { // Tests the network down case for the initial app download and launch. RunAppLaunchNetworkDownTest(); } -IN_PROC_BROWSER_TEST_F(KioskTest, LaunchAppNetworkDown) { +IN_PROC_BROWSER_TEST_P(KioskTest, LaunchAppNetworkDown) { // Tests the network down case for launching an existing app that is // installed in PRE_LaunchAppNetworkDown. RunAppLaunchNetworkDownTest(); } -IN_PROC_BROWSER_TEST_F(KioskTest, LaunchAppWithNetworkConfigAccelerator) { +IN_PROC_BROWSER_TEST_P(KioskTest, LaunchAppWithNetworkConfigAccelerator) { ScopedCanConfigureNetwork can_configure_network(true, false); // Block app loading until the network screen is shown. @@ -946,7 +899,7 @@ WaitForAppLaunchSuccess(); } -IN_PROC_BROWSER_TEST_F(KioskTest, LaunchAppNetworkDownConfigureNotAllowed) { +IN_PROC_BROWSER_TEST_P(KioskTest, LaunchAppNetworkDownConfigureNotAllowed) { // Mock network could not be configured. ScopedCanConfigureNetwork can_configure_network(false, true); @@ -964,7 +917,7 @@ WaitForAppLaunchSuccess(); } -IN_PROC_BROWSER_TEST_F(KioskTest, LaunchAppNetworkPortal) { +IN_PROC_BROWSER_TEST_P(KioskTest, LaunchAppNetworkPortal) { // Mock network could be configured without the owner password. ScopedCanConfigureNetwork can_configure_network(true, false); @@ -983,14 +936,14 @@ WaitForAppLaunchSuccess(); } -IN_PROC_BROWSER_TEST_F(KioskTest, LaunchAppUserCancel) { +IN_PROC_BROWSER_TEST_P(KioskTest, LaunchAppUserCancel) { // Make fake_cws_ return empty update response. set_test_app_version(""); StartAppLaunchFromLoginScreen(SimulateNetworkOfflineClosure()); OobeScreenWaiter splash_waiter(OobeDisplay::SCREEN_APP_LAUNCH_SPLASH); splash_waiter.Wait(); - CrosSettings::Get()->SetBoolean( + settings_helper_.SetBoolean( kAccountsPrefDeviceLocalAccountAutoLoginBailoutEnabled, true); content::WindowedNotificationObserver signal( chrome::NOTIFICATION_APP_TERMINATING, @@ -1002,7 +955,7 @@ chromeos::KioskAppLaunchError::Get()); } -IN_PROC_BROWSER_TEST_F(KioskTest, LaunchInDiagnosticMode) { +IN_PROC_BROWSER_TEST_P(KioskTest, LaunchInDiagnosticMode) { PrepareAppLaunch(); SimulateNetworkOnline(); @@ -1027,7 +980,7 @@ WaitForAppLaunchSuccess(); } -IN_PROC_BROWSER_TEST_F(KioskTest, AutolaunchWarningCancel) { +IN_PROC_BROWSER_TEST_P(KioskTest, AutolaunchWarningCancel) { EnableConsumerKioskMode(); chromeos::WizardController::SkipPostLoginScreensForTesting(); @@ -1059,7 +1012,7 @@ EXPECT_FALSE(KioskAppManager::Get()->IsAutoLaunchEnabled()); } -IN_PROC_BROWSER_TEST_F(KioskTest, AutolaunchWarningConfirm) { +IN_PROC_BROWSER_TEST_P(KioskTest, AutolaunchWarningConfirm) { EnableConsumerKioskMode(); chromeos::WizardController::SkipPostLoginScreensForTesting(); @@ -1098,7 +1051,7 @@ EXPECT_TRUE(app.was_auto_launched_with_zero_delay); } -IN_PROC_BROWSER_TEST_F(KioskTest, KioskEnableCancel) { +IN_PROC_BROWSER_TEST_P(KioskTest, KioskEnableCancel) { chromeos::WizardController::SkipPostLoginScreensForTesting(); chromeos::WizardController* wizard_controller = chromeos::WizardController::default_controller(); @@ -1132,7 +1085,7 @@ GetConsumerKioskModeStatus()); } -IN_PROC_BROWSER_TEST_F(KioskTest, KioskEnableConfirmed) { +IN_PROC_BROWSER_TEST_P(KioskTest, KioskEnableConfirmed) { // Start UI, find menu entry for this app and launch it. chromeos::WizardController::SkipPostLoginScreensForTesting(); chromeos::WizardController* wizard_controller = @@ -1165,7 +1118,7 @@ GetConsumerKioskModeStatus()); } -IN_PROC_BROWSER_TEST_F(KioskTest, KioskEnableAfter2ndSigninScreen) { +IN_PROC_BROWSER_TEST_P(KioskTest, KioskEnableAfter2ndSigninScreen) { chromeos::WizardController::SkipPostLoginScreensForTesting(); chromeos::WizardController* wizard_controller = chromeos::WizardController::default_controller(); @@ -1209,12 +1162,13 @@ content::NotificationService::AllSources()).Wait(); } -IN_PROC_BROWSER_TEST_F(KioskTest, DoNotLaunchWhenUntrusted) { +IN_PROC_BROWSER_TEST_P(KioskTest, DoNotLaunchWhenUntrusted) { PrepareAppLaunch(); SimulateNetworkOnline(); // Make cros settings untrusted. - CrosSettingsPermanentlyUntrustedMaker(); + settings_helper_.SetTrustedStatus( + CrosSettingsProvider::PERMANENTLY_UNTRUSTED); // Check that the attempt to start a kiosk app fails with an error. LaunchApp(test_app_id(), false); @@ -1234,7 +1188,7 @@ // Verifies that a consumer device does not auto-launch kiosk mode when cros // settings are untrusted. -IN_PROC_BROWSER_TEST_F(KioskTest, NoConsumerAutoLaunchWhenUntrusted) { +IN_PROC_BROWSER_TEST_P(KioskTest, NoConsumerAutoLaunchWhenUntrusted) { EnableConsumerKioskMode(); // Wait for and confirm the auto-launch warning. @@ -1253,14 +1207,15 @@ base::FundamentalValue(true)); // Make cros settings untrusted. - CrosSettingsPermanentlyUntrustedMaker(); + settings_helper_.SetTrustedStatus( + CrosSettingsProvider::PERMANENTLY_UNTRUSTED); // Check that the attempt to auto-launch a kiosk app fails with an error. OobeScreenWaiter(OobeDisplay::SCREEN_ERROR_MESSAGE).Wait(); } // Verifies available volumes for kiosk apps in kiosk session. -IN_PROC_BROWSER_TEST_F(KioskTest, GetVolumeList) { +IN_PROC_BROWSER_TEST_P(KioskTest, GetVolumeList) { set_test_app_id(kTestGetVolumeListKioskApp); set_test_app_version("0.1"); set_test_crx_file(test_app_id() + ".crx"); @@ -1272,12 +1227,13 @@ // Verifies that an enterprise device does not auto-launch kiosk mode when cros // settings are untrusted. -IN_PROC_BROWSER_TEST_F(KioskTest, NoEnterpriseAutoLaunchWhenUntrusted) { +IN_PROC_BROWSER_TEST_P(KioskTest, NoEnterpriseAutoLaunchWhenUntrusted) { PrepareAppLaunch(); SimulateNetworkOnline(); // Make cros settings untrusted. - CrosSettingsPermanentlyUntrustedMaker(); + settings_helper_.SetTrustedStatus( + CrosSettingsProvider::PERMANENTLY_UNTRUSTED); // Trigger the code that handles auto-launch on enterprise devices. This would // normally be called from ShowLoginWizard(), which runs so early that it is @@ -1309,7 +1265,19 @@ KioskTest::TearDown(); } - void SetUpOnMainThread() override { KioskTest::SetUpOnMainThread(); } + void SetUpOnMainThread() override { + // For update tests, we cache the app in the PRE part, and then we load it + // in the test, so we need to both store the apps list on teardown (so that + // the app manager would accept existing files in its extension cache on the + // next startup) and copy the list to our stub settings provider as well. + settings_helper_.CopyStoredValue(kAccountsPrefDeviceLocalAccounts); + KioskTest::SetUpOnMainThread(); + } + + void TearDownOnMainThread() override { + settings_helper_.StoreCachedDeviceSetting(kAccountsPrefDeviceLocalAccounts); + KioskTest::TearDownOnMainThread(); + } void PreCacheApp(const std::string& app_id, const std::string& version, @@ -1437,13 +1405,13 @@ DISALLOW_COPY_AND_ASSIGN(KioskUpdateTest); }; -IN_PROC_BROWSER_TEST_F(KioskUpdateTest, PRE_LaunchOfflineEnabledAppNoNetwork) { +IN_PROC_BROWSER_TEST_P(KioskUpdateTest, PRE_LaunchOfflineEnabledAppNoNetwork) { PreCacheAndLaunchApp(kTestOfflineEnabledKioskApp, "1.0.0", std::string(kTestOfflineEnabledKioskApp) + "_v1.crx"); } -IN_PROC_BROWSER_TEST_F(KioskUpdateTest, LaunchOfflineEnabledAppNoNetwork) { +IN_PROC_BROWSER_TEST_P(KioskUpdateTest, LaunchOfflineEnabledAppNoNetwork) { set_test_app_id(kTestOfflineEnabledKioskApp); StartUIForAppLaunch(); SimulateNetworkOffline(); @@ -1453,14 +1421,14 @@ EXPECT_EQ("1.0.0", GetInstalledAppVersion().GetString()); } -IN_PROC_BROWSER_TEST_F(KioskUpdateTest, +IN_PROC_BROWSER_TEST_P(KioskUpdateTest, PRE_LaunchCachedOfflineEnabledAppNoNetwork) { PreCacheApp(kTestOfflineEnabledKioskApp, "1.0.0", std::string(kTestOfflineEnabledKioskApp) + "_v1.crx"); } -IN_PROC_BROWSER_TEST_F(KioskUpdateTest, +IN_PROC_BROWSER_TEST_P(KioskUpdateTest, LaunchCachedOfflineEnabledAppNoNetwork) { set_test_app_id(kTestOfflineEnabledKioskApp); EXPECT_TRUE( @@ -1475,7 +1443,7 @@ // Network offline, app v1.0 has run before, has cached v2.0 crx and v2.0 should // be installed and launched during next launch. -IN_PROC_BROWSER_TEST_F(KioskUpdateTest, +IN_PROC_BROWSER_TEST_P(KioskUpdateTest, PRE_LaunchCachedNewVersionOfflineEnabledAppNoNetwork) { // Install and launch v1 app. PreCacheAndLaunchApp(kTestOfflineEnabledKioskApp, @@ -1488,7 +1456,7 @@ EXPECT_EQ("1.0.0", GetInstalledAppVersion().GetString()); } -IN_PROC_BROWSER_TEST_F(KioskUpdateTest, +IN_PROC_BROWSER_TEST_P(KioskUpdateTest, LaunchCachedNewVersionOfflineEnabledAppNoNetwork) { set_test_app_id(kTestOfflineEnabledKioskApp); EXPECT_TRUE(KioskAppManager::Get()->HasCachedCrx(test_app_id())); @@ -1502,13 +1470,13 @@ EXPECT_EQ("2.0.0", GetInstalledAppVersion().GetString()); } -IN_PROC_BROWSER_TEST_F(KioskUpdateTest, PRE_LaunchOfflineEnabledAppNoUpdate) { +IN_PROC_BROWSER_TEST_P(KioskUpdateTest, PRE_LaunchOfflineEnabledAppNoUpdate) { PreCacheAndLaunchApp(kTestOfflineEnabledKioskApp, "1.0.0", std::string(kTestOfflineEnabledKioskApp) + "_v1.crx"); } -IN_PROC_BROWSER_TEST_F(KioskUpdateTest, LaunchOfflineEnabledAppNoUpdate) { +IN_PROC_BROWSER_TEST_P(KioskUpdateTest, LaunchOfflineEnabledAppNoUpdate) { set_test_app_id(kTestOfflineEnabledKioskApp); fake_cws()->SetNoUpdate(test_app_id()); @@ -1520,13 +1488,13 @@ EXPECT_EQ("1.0.0", GetInstalledAppVersion().GetString()); } -IN_PROC_BROWSER_TEST_F(KioskUpdateTest, PRE_LaunchOfflineEnabledAppHasUpdate) { +IN_PROC_BROWSER_TEST_P(KioskUpdateTest, PRE_LaunchOfflineEnabledAppHasUpdate) { PreCacheAndLaunchApp(kTestOfflineEnabledKioskApp, "1.0.0", std::string(kTestOfflineEnabledKioskApp) + "_v1.crx"); } -IN_PROC_BROWSER_TEST_F(KioskUpdateTest, LaunchOfflineEnabledAppHasUpdate) { +IN_PROC_BROWSER_TEST_P(KioskUpdateTest, LaunchOfflineEnabledAppHasUpdate) { set_test_app_id(kTestOfflineEnabledKioskApp); fake_cws()->SetUpdateCrx( test_app_id(), "ajoggoflpgplnnjkjamcmbepjdjdnpdp.crx", "2.0.0"); @@ -1541,7 +1509,7 @@ // Pre-cache v1 kiosk app, then launch the app without network, // plug in usb stick with a v2 app for offline updating. -IN_PROC_BROWSER_TEST_F(KioskUpdateTest, PRE_UsbStickUpdateAppNoNetwork) { +IN_PROC_BROWSER_TEST_P(KioskUpdateTest, PRE_UsbStickUpdateAppNoNetwork) { PreCacheApp(kTestOfflineEnabledKioskApp, "1.0.0", std::string(kTestOfflineEnabledKioskApp) + "_v1.crx"); @@ -1572,7 +1540,7 @@ } // Restart the device, verify the app has been updated to v2. -IN_PROC_BROWSER_TEST_F(KioskUpdateTest, UsbStickUpdateAppNoNetwork) { +IN_PROC_BROWSER_TEST_P(KioskUpdateTest, UsbStickUpdateAppNoNetwork) { // Verify the kiosk app has been updated to v2. set_test_app_id(kTestOfflineEnabledKioskApp); StartUIForAppLaunch(); @@ -1583,7 +1551,7 @@ } // Usb stick is plugged in without a manifest file on it. -IN_PROC_BROWSER_TEST_F(KioskUpdateTest, UsbStickUpdateAppNoManifest) { +IN_PROC_BROWSER_TEST_P(KioskUpdateTest, UsbStickUpdateAppNoManifest) { PreCacheAndLaunchApp(kTestOfflineEnabledKioskApp, "1.0.0", std::string(kTestOfflineEnabledKioskApp) + "_v1.crx"); @@ -1605,7 +1573,7 @@ } // Usb stick is plugged in with a bad manifest file on it. -IN_PROC_BROWSER_TEST_F(KioskUpdateTest, UsbStickUpdateAppBadManifest) { +IN_PROC_BROWSER_TEST_P(KioskUpdateTest, UsbStickUpdateAppBadManifest) { PreCacheAndLaunchApp(kTestOfflineEnabledKioskApp, "1.0.0", std::string(kTestOfflineEnabledKioskApp) + "_v1.crx"); @@ -1628,7 +1596,7 @@ // Usb stick is plugged in with a lower version of crx file specified in // manifest. -IN_PROC_BROWSER_TEST_F(KioskUpdateTest, UsbStickUpdateAppLowerAppVersion) { +IN_PROC_BROWSER_TEST_P(KioskUpdateTest, UsbStickUpdateAppLowerAppVersion) { // Precache v2 version of app. PreCacheAndLaunchApp(kTestOfflineEnabledKioskApp, "2.0.0", @@ -1652,7 +1620,7 @@ // Usb stick is plugged in with a v1 crx file, although the manifest says // this is a v3 version. -IN_PROC_BROWSER_TEST_F(KioskUpdateTest, UsbStickUpdateAppLowerCrxVersion) { +IN_PROC_BROWSER_TEST_P(KioskUpdateTest, UsbStickUpdateAppLowerCrxVersion) { PreCacheAndLaunchApp(kTestOfflineEnabledKioskApp, "2.0.0", std::string(kTestOfflineEnabledKioskApp) + ".crx"); @@ -1675,7 +1643,7 @@ } // Usb stick is plugged in with a bad crx file. -IN_PROC_BROWSER_TEST_F(KioskUpdateTest, UsbStickUpdateAppBadCrx) { +IN_PROC_BROWSER_TEST_P(KioskUpdateTest, UsbStickUpdateAppBadCrx) { PreCacheAndLaunchApp(kTestOfflineEnabledKioskApp, "1.0.0", std::string(kTestOfflineEnabledKioskApp) + "_v1.crx"); @@ -1697,13 +1665,13 @@ EXPECT_EQ("1.0.0", cached_version); } -IN_PROC_BROWSER_TEST_F(KioskUpdateTest, PRE_PermissionChange) { +IN_PROC_BROWSER_TEST_P(KioskUpdateTest, PRE_PermissionChange) { PreCacheAndLaunchApp(kTestOfflineEnabledKioskApp, "2.0.0", std::string(kTestOfflineEnabledKioskApp) + ".crx"); } -IN_PROC_BROWSER_TEST_F(KioskUpdateTest, PermissionChange) { +IN_PROC_BROWSER_TEST_P(KioskUpdateTest, PermissionChange) { set_test_app_id(kTestOfflineEnabledKioskApp); set_test_app_version("2.0.0"); set_test_crx_file(test_app_id() + "_v2_permission_change.crx"); @@ -1716,7 +1684,7 @@ EXPECT_EQ("2.0.0", GetInstalledAppVersion().GetString()); } -IN_PROC_BROWSER_TEST_F(KioskUpdateTest, PRE_PreserveLocalData) { +IN_PROC_BROWSER_TEST_P(KioskUpdateTest, PRE_PreserveLocalData) { // Installs v1 app and writes some local data. set_test_app_id(kTestLocalFsKioskApp); set_test_app_version("1.0.0"); @@ -1729,7 +1697,7 @@ ASSERT_TRUE(catcher.GetNextResult()) << catcher.message(); } -IN_PROC_BROWSER_TEST_F(KioskUpdateTest, PreserveLocalData) { +IN_PROC_BROWSER_TEST_P(KioskUpdateTest, PreserveLocalData) { // Update existing v1 app installed in PRE_PreserveLocalData to v2 // that reads and verifies the local data. set_test_app_id(kTestLocalFsKioskApp); @@ -1751,8 +1719,9 @@ } void SetUpInProcessBrowserTestFixture() override { - device_policy_test_helper_.MarkAsEnterpriseOwned(); - device_policy_test_helper_.InstallOwnerKey(); + policy::DevicePolicyCrosTestHelper::MarkAsEnterpriseOwnedBy( + kTestOwnerEmail); + settings_helper_.SetCurrentUserIsOwner(false); KioskTest::SetUpInProcessBrowserTestFixture(); } @@ -1795,47 +1764,27 @@ base::RunLoop().RunUntilIdle(); } - static void StorePolicyCallback(const base::Closure& callback, bool result) { - ASSERT_TRUE(result); - callback.Run(); - } - void ConfigureKioskAppInPolicy(const std::string& account_id, const std::string& app_id, const std::string& update_url) { - em::DeviceLocalAccountsProto* accounts = - device_policy_test_helper_.device_policy()->payload() - .mutable_device_local_accounts(); - em::DeviceLocalAccountInfoProto* account = accounts->add_account(); - account->set_account_id(account_id); - account->set_type( - em::DeviceLocalAccountInfoProto::ACCOUNT_TYPE_KIOSK_APP); - account->mutable_kiosk_app()->set_app_id(app_id); - if (!update_url.empty()) - account->mutable_kiosk_app()->set_update_url(update_url); - accounts->set_auto_login_id(account_id); - em::PolicyData& policy_data = - device_policy_test_helper_.device_policy()->policy_data(); - policy_data.set_service_account_identity(kTestEnterpriseServiceAccountId); - device_policy_test_helper_.device_policy()->Build(); - - base::RunLoop run_loop; - DBusThreadManager::Get()->GetSessionManagerClient()->StoreDevicePolicy( - device_policy_test_helper_.device_policy()->GetBlob(), - base::Bind(&KioskEnterpriseTest::StorePolicyCallback, - run_loop.QuitClosure())); - run_loop.Run(); - - DeviceSettingsService::Get()->Load(); + settings_helper_.SetCurrentUserIsOwner(true); + std::vector<policy::DeviceLocalAccount> accounts; + accounts.push_back( + policy::DeviceLocalAccount(policy::DeviceLocalAccount::TYPE_KIOSK_APP, + account_id, app_id, update_url)); + policy::SetDeviceLocalAccounts(owner_settings_service_.get(), accounts); + settings_helper_.SetString(kAccountsPrefDeviceLocalAccountAutoLoginId, + account_id); + settings_helper_.SetString(kServiceAccountIdentity, + kTestEnterpriseServiceAccountId); + settings_helper_.SetCurrentUserIsOwner(false); } - policy::DevicePolicyCrosTestHelper device_policy_test_helper_; - private: DISALLOW_COPY_AND_ASSIGN(KioskEnterpriseTest); }; -IN_PROC_BROWSER_TEST_F(KioskEnterpriseTest, EnterpriseKioskApp) { +IN_PROC_BROWSER_TEST_P(KioskEnterpriseTest, EnterpriseKioskApp) { // Prepare Fake CWS to serve app crx. set_test_app_id(kTestEnterpriseKioskApp); set_test_app_version("1.0.0"); @@ -1890,7 +1839,7 @@ content::RunAllPendingInMessageLoop(); } -IN_PROC_BROWSER_TEST_F(KioskEnterpriseTest, PrivateStore) { +IN_PROC_BROWSER_TEST_P(KioskEnterpriseTest, PrivateStore) { set_test_app_id(kTestEnterpriseKioskApp); const char kPrivateStoreUpdate[] = "/private_store_update"; @@ -1975,7 +1924,7 @@ DISALLOW_COPY_AND_ASSIGN(KioskHiddenWebUITest); }; -IN_PROC_BROWSER_TEST_F(KioskHiddenWebUITest, AutolaunchWarning) { +IN_PROC_BROWSER_TEST_P(KioskHiddenWebUITest, AutolaunchWarning) { // Add a device owner. FakeChromeUserManager* user_manager = new FakeChromeUserManager(); user_manager->AddUser(kTestOwnerEmail); @@ -2006,4 +1955,13 @@ EXPECT_TRUE(wallpaper_loaded()); } +INSTANTIATE_TEST_CASE_P(KioskSuite, KioskTest, testing::Bool()); +INSTANTIATE_TEST_CASE_P(KioskUpdateSuite, KioskUpdateTest, testing::Bool()); +INSTANTIATE_TEST_CASE_P(KioskEnterpriseSuite, + KioskEnterpriseTest, + testing::Bool()); +INSTANTIATE_TEST_CASE_P(KioskHiddenWebUISuite, + KioskHiddenWebUITest, + testing::Bool()); + } // namespace chromeos
diff --git a/chrome/browser/chromeos/login/login_manager_test.cc b/chrome/browser/chromeos/login/login_manager_test.cc index 89cf712..c58af0c 100644 --- a/chrome/browser/chromeos/login/login_manager_test.cc +++ b/chrome/browser/chromeos/login/login_manager_test.cc
@@ -33,7 +33,7 @@ namespace chromeos { LoginManagerTest::LoginManagerTest(bool should_launch_browser) - : use_webview_(false), + : use_webview_(true), should_launch_browser_(should_launch_browser), web_contents_(NULL) { set_exit_when_last_browser_closes(false);
diff --git a/chrome/browser/chromeos/login/login_utils_browsertest.cc b/chrome/browser/chromeos/login/login_utils_browsertest.cc index 09eb71e..6025228 100644 --- a/chrome/browser/chromeos/login/login_utils_browsertest.cc +++ b/chrome/browser/chromeos/login/login_utils_browsertest.cc
@@ -11,6 +11,7 @@ #include "chrome/browser/browser_process.h" #include "chrome/browser/chrome_notification_types.h" #include "chrome/browser/chromeos/login/existing_user_controller.h" +#include "chrome/browser/chromeos/login/test/oobe_base_test.h" #include "chrome/browser/chromeos/login/ui/webui_login_display.h" #include "chrome/browser/chromeos/login/wizard_controller.h" #include "chrome/browser/rlz/rlz.h" @@ -42,39 +43,10 @@ } // namespace -class LoginUtilsTest : public InProcessBrowserTest { +class LoginUtilsTest : public OobeBaseTest, + public testing::WithParamInterface<bool> { public: - LoginUtilsTest() {} - - void SetUpCommandLine(base::CommandLine* command_line) override { - // Initialize the test server early, so that we can use its base url for - // the command line flags. - ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); - - // Use the login manager screens and the gaia auth extension. - command_line->AppendSwitch(switches::kLoginManager); - command_line->AppendSwitch(switches::kForceLoginManagerInTests); - command_line->AppendSwitchASCII(switches::kLoginProfile, "user"); - command_line->AppendSwitchASCII(::switches::kAuthExtensionPath, - "gaia_auth"); - - // Redirect requests to gaia and the policy server to the test server. - command_line->AppendSwitchASCII(::switches::kGaiaUrl, - embedded_test_server()->base_url().spec()); - command_line->AppendSwitchASCII(::switches::kLsoUrl, - embedded_test_server()->base_url().spec()); - } - - void SetUpOnMainThread() override { - fake_gaia_.Initialize(); - embedded_test_server()->RegisterRequestHandler( - base::Bind(&FakeGaia::HandleRequest, base::Unretained(&fake_gaia_))); - } - - void TearDownOnMainThread() override { - RunUntilIdle(); - EXPECT_TRUE(embedded_test_server()->ShutdownAndWaitUntilComplete()); - } + LoginUtilsTest() { set_use_webview(GetParam()); } void RunUntilIdle() { base::RunLoop().RunUntilIdle(); @@ -104,23 +76,12 @@ } private: - FakeGaia fake_gaia_; - DISALLOW_COPY_AND_ASSIGN(LoginUtilsTest); }; #if defined(ENABLE_RLZ) -IN_PROC_BROWSER_TEST_F(LoginUtilsTest, RlzInitialized) { - // Skip to the signin screen. - WizardController::SkipPostLoginScreensForTesting(); - WizardController* wizard_controller = WizardController::default_controller(); - ASSERT_TRUE(wizard_controller); - wizard_controller->SkipToLoginForTesting(LoginScreenContext()); - - content::WindowedNotificationObserver( - chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE, - content::NotificationService::AllSources()).Wait(); - RunUntilIdle(); +IN_PROC_BROWSER_TEST_P(LoginUtilsTest, RlzInitialized) { + WaitForSigninScreen(); // No RLZ brand code set initially. EXPECT_FALSE(local_state()->HasPrefPath(prefs::kRLZBrand)); @@ -154,6 +115,9 @@ EXPECT_EQ(base::string16(), rlz_string); } } + +INSTANTIATE_TEST_CASE_P(LoginUtilsTestSuite, LoginUtilsTest, testing::Bool()); + #endif } // namespace chromeos
diff --git a/chrome/browser/chromeos/login/proxy_auth_dialog_browsertest.cc b/chrome/browser/chromeos/login/proxy_auth_dialog_browsertest.cc index 3eef72f..9fb5726 100644 --- a/chrome/browser/chromeos/login/proxy_auth_dialog_browsertest.cc +++ b/chrome/browser/chromeos/login/proxy_auth_dialog_browsertest.cc
@@ -68,9 +68,7 @@ proxy_server_(net::SpawnedTestServer::TYPE_BASIC_AUTH_PROXY, net::SpawnedTestServer::kLocalhost, base::FilePath()) { - // TODO(paulmeyer): Re-enable webview version of this test - // (uncomment this line) once http://crbug.com/452452 is fixed. - // set_use_webview(GetParam()); + set_use_webview(GetParam()); } ~ProxyAuthOnUserBoardScreenTest() override {} @@ -128,8 +126,10 @@ } } +// TODO(paulmeyer): Re-enable webview version of this test +// (once http://crbug.com/452452 is fixed. INSTANTIATE_TEST_CASE_P(ProxyAuthOnUserBoardScreenTestSuite, ProxyAuthOnUserBoardScreenTest, - testing::Bool()); + testing::Values(false)); } // namespace chromeos
diff --git a/chrome/browser/chromeos/login/screen_manager.h b/chrome/browser/chromeos/login/screen_manager.h index 7a18ba65..2473126 100644 --- a/chrome/browser/chromeos/login/screen_manager.h +++ b/chrome/browser/chromeos/login/screen_manager.h
@@ -22,7 +22,7 @@ virtual ~ScreenManager(); // Getter for screen with lazy initialization. - BaseScreen* GetScreen(const std::string& screen_name); + virtual BaseScreen* GetScreen(const std::string& screen_name); // Factory for screen instances. virtual BaseScreen* CreateScreen(const std::string& screen_name) = 0;
diff --git a/chrome/browser/chromeos/login/screens/host_pairing_screen.cc b/chrome/browser/chromeos/login/screens/host_pairing_screen.cc index a2921e2..fba3190 100644 --- a/chrome/browser/chromeos/login/screens/host_pairing_screen.cc +++ b/chrome/browser/chromeos/login/screens/host_pairing_screen.cc
@@ -84,24 +84,30 @@ CommitContextChanges(); } -void HostPairingScreen::ConfigureHost(bool accepted_eula, - const std::string& lang, - const std::string& timezone, - bool send_reports, - const std::string& keyboard_layout) { +void HostPairingScreen::ConfigureHostRequested( + bool accepted_eula, + const std::string& lang, + const std::string& timezone, + bool send_reports, + const std::string& keyboard_layout) { VLOG(1) << "ConfigureHostMessage language=" << lang << ", timezone=" << timezone << ", keyboard_layout=" << keyboard_layout; remora_controller_->RemoveObserver(this); if (delegate_) { - delegate_->ConfigureHost( + delegate_->ConfigureHostRequested( accepted_eula, lang, timezone, send_reports, keyboard_layout); } Finish(WizardController::HOST_PAIRING_FINISHED); } -void HostPairingScreen::EnrollHost(const std::string& auth_token) { +void HostPairingScreen::AddNetworkRequested(const std::string& onc_spec) { + if (delegate_) + delegate_->AddNetworkRequested(onc_spec); +} + +void HostPairingScreen::EnrollHostRequested(const std::string& auth_token) { NOTREACHED(); }
diff --git a/chrome/browser/chromeos/login/screens/host_pairing_screen.h b/chrome/browser/chromeos/login/screens/host_pairing_screen.h index 37c4598..73df4b8 100644 --- a/chrome/browser/chromeos/login/screens/host_pairing_screen.h +++ b/chrome/browser/chromeos/login/screens/host_pairing_screen.h
@@ -21,11 +21,18 @@ class Delegate { public: virtual ~Delegate() {} - virtual void ConfigureHost(bool accepted_eula, - const std::string& lang, - const std::string& timezone, - bool send_reports, - const std::string& keyboard_layout) = 0; + + // Called when a configuration has been received, and should be applied to + // this device. + virtual void ConfigureHostRequested(bool accepted_eula, + const std::string& lang, + const std::string& timezone, + bool send_reports, + const std::string& keyboard_layout) = 0; + + // Called when a network configuration has been received, and should be + // used on this device. + virtual void AddNetworkRequested(const std::string& onc_spec) = 0; }; HostPairingScreen(BaseScreenDelegate* base_screen_delegate, @@ -47,12 +54,13 @@ // pairing_chromeos::HostPairingController::Observer: void PairingStageChanged(Stage new_stage) override; - void ConfigureHost(bool accepted_eula, - const std::string& lang, - const std::string& timezone, - bool send_reports, - const std::string& keyboard_layout) override; - void EnrollHost(const std::string& auth_token) override; + void ConfigureHostRequested(bool accepted_eula, + const std::string& lang, + const std::string& timezone, + bool send_reports, + const std::string& keyboard_layout) override; + void AddNetworkRequested(const std::string& onc_spec) override; + void EnrollHostRequested(const std::string& auth_token) override; // Overridden from ControllerPairingView::Delegate: void OnActorDestroyed(HostPairingScreenActor* actor) override;
diff --git a/chrome/browser/chromeos/login/screens/network_screen.cc b/chrome/browser/chromeos/login/screens/network_screen.cc index f451b52..13c6afa 100644 --- a/chrome/browser/chromeos/login/screens/network_screen.cc +++ b/chrome/browser/chromeos/login/screens/network_screen.cc
@@ -224,6 +224,10 @@ return timezone_; } +void NetworkScreen::CreateNetworkFromOnc(const std::string& onc_spec) { + network_state_helper_->CreateNetworkFromOnc(onc_spec); +} + void NetworkScreen::AddObserver(Observer* observer) { if (observer) observers_.AddObserver(observer); @@ -370,7 +374,7 @@ scoped_ptr<base::ListValue> new_language_list, std::string new_language_list_locale, std::string new_selected_language) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); language_list_.reset(new_language_list.release()); language_list_locale_ = new_language_list_locale;
diff --git a/chrome/browser/chromeos/login/screens/network_screen.h b/chrome/browser/chromeos/login/screens/network_screen.h index 5c8cc2d..9c1ae66f 100644 --- a/chrome/browser/chromeos/login/screens/network_screen.h +++ b/chrome/browser/chromeos/login/screens/network_screen.h
@@ -88,6 +88,8 @@ void SetTimezone(const std::string& timezone_id); std::string GetTimezone() const; + void CreateNetworkFromOnc(const std::string& onc_spec); + void AddObserver(Observer* observer); void RemoveObserver(Observer* observer);
diff --git a/chrome/browser/chromeos/login/screens/user_image_screen.cc b/chrome/browser/chromeos/login/screens/user_image_screen.cc index d62c9702..56063c9 100644 --- a/chrome/browser/chromeos/login/screens/user_image_screen.cc +++ b/chrome/browser/chromeos/login/screens/user_image_screen.cc
@@ -66,8 +66,6 @@ UserImageScreen::UserImageScreen(BaseScreenDelegate* base_screen_delegate, UserImageView* view) : UserImageModel(base_screen_delegate), - ImageRequest( - BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI)), view_(view), accept_photo_after_decoding_(false), selected_image_(user_manager::User::USER_IMAGE_INVALID),
diff --git a/chrome/browser/chromeos/login/screenshot_testing/screenshot_tester.cc b/chrome/browser/chromeos/login/screenshot_testing/screenshot_tester.cc index df9427e..0f7c591 100644 --- a/chrome/browser/chromeos/login/screenshot_testing/screenshot_tester.cc +++ b/chrome/browser/chromeos/login/screenshot_testing/screenshot_tester.cc
@@ -203,7 +203,7 @@ } ScreenshotTester::PNGFile ScreenshotTester::TakeScreenshot() { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); aura::Window* primary_window = ash::Shell::GetPrimaryRootWindow(); gfx::Rect rect = primary_window->bounds(); PNGFile screenshot = new base::RefCountedBytes;
diff --git a/chrome/browser/chromeos/login/screenshot_testing/screenshot_testing_mixin.cc b/chrome/browser/chromeos/login/screenshot_testing/screenshot_testing_mixin.cc index afb3c6e..de641da 100644 --- a/chrome/browser/chromeos/login/screenshot_testing/screenshot_testing_mixin.cc +++ b/chrome/browser/chromeos/login/screenshot_testing/screenshot_testing_mixin.cc
@@ -27,8 +27,7 @@ void ScreenshotTestingMixin::SetUpCommandLine(base::CommandLine* command_line) { if (enable_test_screenshots_) { command_line->AppendSwitch(switches::kEnablePixelOutputInTests); - } else { - command_line->AppendSwitch(switches::kUIDisableImplSidePainting); + command_line->AppendSwitch(switches::kUIEnableImplSidePainting); } }
diff --git a/chrome/browser/chromeos/login/session/user_session_manager.cc b/chrome/browser/chromeos/login/session/user_session_manager.cc index 01d04f1d..1fbb3d8 100644 --- a/chrome/browser/chromeos/login/session/user_session_manager.cc +++ b/chrome/browser/chromeos/login/session/user_session_manager.cc
@@ -476,12 +476,12 @@ } bool UserSessionManager::UserSessionsRestored() const { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); return user_sessions_restored_; } bool UserSessionManager::UserSessionsRestoreInProgress() const { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); return user_sessions_restore_in_progress_; } @@ -673,13 +673,13 @@ void UserSessionManager::AddSessionStateObserver( chromeos::UserSessionStateObserver* observer) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); session_state_observer_list_.AddObserver(observer); } void UserSessionManager::RemoveSessionStateObserver( chromeos::UserSessionStateObserver* observer) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); session_state_observer_list_.RemoveObserver(observer); } @@ -788,15 +788,16 @@ RestorePendingUserSessions(); } -void UserSessionManager::ChildAccountStatusReceivedCallback() { - StopChildStatusObserving(); +void UserSessionManager::ChildAccountStatusReceivedCallback(Profile* profile) { + StopChildStatusObserving(profile); } -void UserSessionManager::StopChildStatusObserving() { - if (!waiting_for_child_account_status_) { +void UserSessionManager::StopChildStatusObserving(Profile* profile) { + if (!waiting_for_child_account_status_ && + !SessionStartupPref::TypeIsManaged(profile->GetPrefs())) { InitializeStartUrls(); - waiting_for_child_account_status_ = false; } + waiting_for_child_account_status_ = false; } void UserSessionManager::CreateUserSession(const UserContext& user_context, @@ -1149,13 +1150,12 @@ bool UserSessionManager::InitializeUserSession(Profile* profile) { ChildAccountService* child_service = ChildAccountServiceFactory::GetForProfile(profile); - child_service->AddChildStatusReceivedCallback(base::Bind( - &UserSessionManager::ChildAccountStatusReceivedCallback, - weak_factory_.GetWeakPtr())); + child_service->AddChildStatusReceivedCallback( + base::Bind(&UserSessionManager::ChildAccountStatusReceivedCallback, + weak_factory_.GetWeakPtr(), profile)); base::MessageLoopProxy::current()->PostDelayedTask( - FROM_HERE, - base::Bind(&UserSessionManager::StopChildStatusObserving, - weak_factory_.GetWeakPtr()), + FROM_HERE, base::Bind(&UserSessionManager::StopChildStatusObserving, + weak_factory_.GetWeakPtr(), profile), base::TimeDelta::FromMilliseconds(kFlagsFetchingLoginTimeoutMs)); user_manager::UserManager* user_manager = user_manager::UserManager::Get(); @@ -1254,8 +1254,14 @@ // Authentication request context may not be available if user was not // signing in with GAIA webview (i.e. webview instance hasn't been - // initialized at all). Use fallback request context. - if (!auth_request_context) { + // initialized at all). Use fallback request context if authenticator was + // provided. + // Authenticator instance may not be initialized for session + // restore case when Chrome is restarting after crash or to apply custom user + // flags. In that case auth_request_context will be nullptr which is accepted + // by RestoreSession() for session restore case. + if (!auth_request_context && + (authenticator_.get() && authenticator_->authentication_context())) { auth_request_context = authenticator_->authentication_context()->GetRequestContext(); }
diff --git a/chrome/browser/chromeos/login/session/user_session_manager.h b/chrome/browser/chromeos/login/session/user_session_manager.h index 9be9b718..fc5f779 100644 --- a/chrome/browser/chromeos/login/session/user_session_manager.h +++ b/chrome/browser/chromeos/login/session/user_session_manager.h
@@ -254,9 +254,9 @@ // Used when restoring user sessions after crash. void OnProfilePrepared(Profile* profile, bool browser_launched) override; - void ChildAccountStatusReceivedCallback(); + void ChildAccountStatusReceivedCallback(Profile* profile); - void StopChildStatusObserving(); + void StopChildStatusObserving(Profile* profile); void CreateUserSession(const UserContext& user_context, bool has_auth_cookies);
diff --git a/chrome/browser/chromeos/login/signin/oauth2_login_manager.h b/chrome/browser/chromeos/login/signin/oauth2_login_manager.h index 4197cc8..15b3473 100644 --- a/chrome/browser/chromeos/login/signin/oauth2_login_manager.h +++ b/chrome/browser/chromeos/login/signin/oauth2_login_manager.h
@@ -90,6 +90,8 @@ // RESTORE_FROM_AUTH_CODE, respectively // parameters |oauth2_refresh_token| or |auth_code| need to have non-empty // value. + // For |restore_strategy| with values RESTORE_FROM_COOKIE_JAR or + // RESTORE_FROM_AUTH_CODE |auth_request_context| must be initialized. void RestoreSession( net::URLRequestContextGetter* auth_request_context, SessionRestoreStrategy restore_strategy,
diff --git a/chrome/browser/chromeos/login/startup_utils.cc b/chrome/browser/chromeos/login/startup_utils.cc index c136ea3..ec108c5c 100644 --- a/chrome/browser/chromeos/login/startup_utils.cc +++ b/chrome/browser/chromeos/login/startup_utils.cc
@@ -61,6 +61,7 @@ registry->RegisterStringPref(prefs::kInitialLocale, "en-US"); registry->RegisterBooleanPref(prefs::kNewOobe, false); registry->RegisterBooleanPref(prefs::kWebviewSigninDisabled, false); + registry->RegisterBooleanPref(prefs::kNewLoginUIPopup, false); } // static
diff --git a/chrome/browser/chromeos/login/supervised/supervised_user_creation_screen.cc b/chrome/browser/chromeos/login/supervised/supervised_user_creation_screen.cc index 7e784d65..041be37 100644 --- a/chrome/browser/chromeos/login/supervised/supervised_user_creation_screen.cc +++ b/chrome/browser/chromeos/login/supervised/supervised_user_creation_screen.cc
@@ -100,8 +100,6 @@ BaseScreenDelegate* base_screen_delegate, SupervisedUserCreationScreenHandler* actor) : BaseScreen(base_screen_delegate), - ImageRequest(content::BrowserThread::GetMessageLoopProxyForThread( - content::BrowserThread::UI)), actor_(actor), on_error_screen_(false), manager_signin_in_progress_(false), @@ -212,6 +210,10 @@ controller_->FinishCreation(); } +void SupervisedUserCreationScreen::HideFlow() { + Hide(); +} + void SupervisedUserCreationScreen::AuthenticateManager( const std::string& manager_id, const std::string& manager_password) { @@ -589,7 +591,7 @@ void SupervisedUserCreationScreen::OnPhotoTaken( const std::string& raw_data) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); user_photo_ = gfx::ImageSkia(); ImageDecoder::Cancel(this); ImageDecoder::Start(this, raw_data);
diff --git a/chrome/browser/chromeos/login/supervised/supervised_user_creation_screen.h b/chrome/browser/chromeos/login/supervised/supervised_user_creation_screen.h index 62bf1ea..d51a8ed 100644 --- a/chrome/browser/chromeos/login/supervised/supervised_user_creation_screen.h +++ b/chrome/browser/chromeos/login/supervised/supervised_user_creation_screen.h
@@ -92,6 +92,7 @@ const std::string& manager_password) override; void AbortFlow() override; void FinishFlow() override; + void HideFlow() override; bool FindUserByDisplayName(const base::string16& display_name, std::string* out_id) const override; void OnPageSelected(const std::string& page) override;
diff --git a/chrome/browser/chromeos/login/test/oobe_base_test.cc b/chrome/browser/chromeos/login/test/oobe_base_test.cc index 5aa0eff..1851974e 100644 --- a/chrome/browser/chromeos/login/test/oobe_base_test.cc +++ b/chrome/browser/chromeos/login/test/oobe_base_test.cc
@@ -44,7 +44,7 @@ network_portal_detector_(NULL), needs_background_networking_(false), gaia_frame_parent_("signin-frame"), - use_webview_(false), + use_webview_(true), initialize_fake_merge_session_(true) { set_exit_when_last_browser_closes(false); set_chromeos_user_ = false;
diff --git a/chrome/browser/chromeos/login/ui/captive_portal_window_browsertest.cc b/chrome/browser/chromeos/login/ui/captive_portal_window_browsertest.cc index 50432bd..50f9670 100644 --- a/chrome/browser/chromeos/login/ui/captive_portal_window_browsertest.cc +++ b/chrome/browser/chromeos/login/ui/captive_portal_window_browsertest.cc
@@ -218,18 +218,17 @@ ASSERT_TRUE(host); OobeUI* oobe = host->GetOobeUI(); ASSERT_TRUE(oobe); - NetworkErrorView* network_error_view = oobe->GetNetworkErrorView(); - ASSERT_TRUE(network_error_view); // Error screen asks portal detector to change detection strategy. - ErrorScreen error_screen(NULL, network_error_view); + ErrorScreen* error_screen = oobe->GetErrorScreen(); + ASSERT_TRUE(error_screen); ASSERT_EQ(PortalDetectorStrategy::STRATEGY_ID_LOGIN_SCREEN, strategy_id()); network_portal_detector()->NotifyObserversForTesting(); OobeScreenWaiter(OobeDisplay::SCREEN_ERROR_MESSAGE).Wait(); ASSERT_EQ(PortalDetectorStrategy::STRATEGY_ID_ERROR_SCREEN, strategy_id()); - error_screen.ShowCaptivePortal(); + error_screen->ShowCaptivePortal(); } } // namespace chromeos
diff --git a/chrome/browser/chromeos/login/ui/login_web_dialog.cc b/chrome/browser/chromeos/login/ui/login_web_dialog.cc index 4c3a0dd..c46692f 100644 --- a/chrome/browser/chromeos/login/ui/login_web_dialog.cc +++ b/chrome/browser/chromeos/login/ui/login_web_dialog.cc
@@ -137,8 +137,7 @@ void LoginWebDialog::OnCloseContents(WebContents* source, bool* out_close_dialog) { - if (out_close_dialog) - *out_close_dialog = true; + *out_close_dialog = true; if (g_web_contents_stack.Pointer()->size() && source == g_web_contents_stack.Pointer()->front()) {
diff --git a/chrome/browser/chromeos/login/ui/oobe_display.h b/chrome/browser/chromeos/login/ui/oobe_display.h index 1bf92f38..75f6614b 100644 --- a/chrome/browser/chromeos/login/ui/oobe_display.h +++ b/chrome/browser/chromeos/login/ui/oobe_display.h
@@ -19,6 +19,7 @@ class DeviceDisabledScreenActor; class EnableDebuggingScreenActor; class EnrollmentScreenActor; +class ErrorScreen; class EulaView; class GaiaScreenHandler; class HIDDetectionView; @@ -84,7 +85,7 @@ virtual KioskEnableScreenActor* GetKioskEnableScreenActor() = 0; virtual TermsOfServiceScreenActor* GetTermsOfServiceScreenActor() = 0; virtual UserImageView* GetUserImageView() = 0; - virtual NetworkErrorView* GetNetworkErrorView() = 0; + virtual ErrorScreen* GetErrorScreen() = 0; virtual WrongHWIDScreenActor* GetWrongHWIDScreenActor() = 0; virtual AutoEnrollmentCheckScreenActor* GetAutoEnrollmentCheckScreenActor() = 0;
diff --git a/chrome/browser/chromeos/login/ui/proxy_settings_dialog.cc b/chrome/browser/chromeos/login/ui/proxy_settings_dialog.cc index a91b2e3..7bb0765ca 100644 --- a/chrome/browser/chromeos/login/ui/proxy_settings_dialog.cc +++ b/chrome/browser/chromeos/login/ui/proxy_settings_dialog.cc
@@ -62,7 +62,7 @@ window, base::string16(), GetURLForProxySettings(network.guid())) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); ++instance_count_; gfx::Rect screen_bounds(chromeos::CalculateScreenBounds(gfx::Size())); @@ -84,7 +84,7 @@ } ProxySettingsDialog::~ProxySettingsDialog() { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); --instance_count_; }
diff --git a/chrome/browser/chromeos/login/ui/webui_login_view.cc b/chrome/browser/chromeos/login/ui/webui_login_view.cc index ccf60bf..b03a8a33 100644 --- a/chrome/browser/chromeos/login/ui/webui_login_view.cc +++ b/chrome/browser/chromeos/login/ui/webui_login_view.cc
@@ -70,6 +70,7 @@ const char kAccelNameAppLaunchNetworkConfig[] = "app_launch_network_config"; const char kAccelNameNewOobe[] = "new_oobe"; const char kAccelNameToggleWebviewSignin[] = "toggle_webview_signin"; +const char kAccelNameToggleNewLoginUI[] = "toggle_new_login_ui"; const char kAccelNameToggleEasyBootstrap[] = "toggle_easy_bootstrap"; // A class to change arrow key traversal behavior when it's alive. @@ -134,6 +135,9 @@ ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN | ui::EF_SHIFT_DOWN)] = kAccelNameToggleWebviewSignin; accel_map_[ui::Accelerator( + ui::VKEY_L, ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN | ui::EF_SHIFT_DOWN)] = + kAccelNameToggleNewLoginUI; + accel_map_[ui::Accelerator( ui::VKEY_B, ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN | ui::EF_SHIFT_DOWN)] = kAccelNameToggleEasyBootstrap;
diff --git a/chrome/browser/chromeos/login/users/avatar/user_image_manager_test_util.cc b/chrome/browser/chromeos/login/users/avatar/user_image_manager_test_util.cc index 8860df4..a5fb1fc8 100644 --- a/chrome/browser/chromeos/login/users/avatar/user_image_manager_test_util.cc +++ b/chrome/browser/chromeos/login/users/avatar/user_image_manager_test_util.cc
@@ -44,8 +44,7 @@ return true; } -ImageLoader::ImageLoader(const base::FilePath& path) - : ImageRequest(base::MessageLoopProxy::current()), path_(path) { +ImageLoader::ImageLoader(const base::FilePath& path) : path_(path) { } ImageLoader::~ImageLoader() {
diff --git a/chrome/browser/chromeos/login/users/user_manager_unittest.cc b/chrome/browser/chromeos/login/users/user_manager_unittest.cc index 3765efa..b2e6368 100644 --- a/chrome/browser/chromeos/login/users/user_manager_unittest.cc +++ b/chrome/browser/chromeos/login/users/user_manager_unittest.cc
@@ -11,14 +11,11 @@ #include "base/memory/scoped_ptr.h" #include "base/prefs/pref_service.h" #include "base/run_loop.h" -#include "base/values.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/chromeos/login/users/chrome_user_manager_impl.h" #include "chrome/browser/chromeos/login/users/scoped_user_manager_enabler.h" #include "chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager.h" -#include "chrome/browser/chromeos/settings/cros_settings.h" -#include "chrome/browser/chromeos/settings/device_settings_service.h" -#include "chrome/browser/chromeos/settings/stub_cros_settings_provider.h" +#include "chrome/browser/chromeos/settings/scoped_cros_settings_test_helper.h" #include "chrome/browser/profiles/profile_manager.h" #include "chrome/test/base/scoped_testing_local_state.h" #include "chrome/test/base/testing_browser_process.h" @@ -26,7 +23,6 @@ #include "chromeos/chromeos_switches.h" #include "chromeos/dbus/dbus_thread_manager.h" #include "chromeos/settings/cros_settings_names.h" -#include "chromeos/settings/cros_settings_provider.h" #include "components/user_manager/user.h" #include "components/user_manager/user_manager.h" #include "content/public/common/content_switches.h" @@ -50,7 +46,6 @@ } }; - class UserManagerTest : public testing::Test { protected: void SetUp() override { @@ -59,15 +54,7 @@ command_line.AppendSwitch( chromeos::switches::kIgnoreUserProfileMappingForTests); - cros_settings_ = CrosSettings::Get(); - - // Replace the real DeviceSettingsProvider with a stub. - device_settings_provider_ = - cros_settings_->GetProvider(chromeos::kReportDeviceVersionInfo); - EXPECT_TRUE(device_settings_provider_); - EXPECT_TRUE( - cros_settings_->RemoveSettingsProvider(device_settings_provider_)); - cros_settings_->AddSettingsProvider(&stub_settings_provider_); + settings_helper_.ReplaceProvider(kDeviceOwner); // Populate the stub DeviceSettingsProvider with valid values. SetDeviceSettings(false, "", false); @@ -90,11 +77,6 @@ // Unregister the in-memory local settings instance. local_state_.reset(); - // Restore the real DeviceSettingsProvider. - EXPECT_TRUE( - cros_settings_->RemoveSettingsProvider(&stub_settings_provider_)); - cros_settings_->AddSettingsProvider(device_settings_provider_); - // Shut down the DeviceSettingsService. DeviceSettingsService::Get()->UnsetSessionManager(); TestingBrowserProcess::GetGlobal()->SetProfileManager(NULL); @@ -140,14 +122,11 @@ void SetDeviceSettings(bool ephemeral_users_enabled, const std::string &owner, bool supervised_users_enabled) { - base::FundamentalValue - ephemeral_users_enabled_value(ephemeral_users_enabled); - stub_settings_provider_.Set(kAccountsPrefEphemeralUsersEnabled, - ephemeral_users_enabled_value); - base::StringValue owner_value(owner); - stub_settings_provider_.Set(kDeviceOwner, owner_value); - stub_settings_provider_.Set(kAccountsPrefSupervisedUsersEnabled, - base::FundamentalValue(supervised_users_enabled)); + settings_helper_.SetBoolean(kAccountsPrefEphemeralUsersEnabled, + ephemeral_users_enabled); + settings_helper_.SetString(kDeviceOwner, owner); + settings_helper_.SetBoolean(kAccountsPrefSupervisedUsersEnabled, + supervised_users_enabled); } void RetrieveTrustedDevicePolicies() { @@ -157,14 +136,9 @@ protected: content::TestBrowserThreadBundle thread_bundle_; - CrosSettings* cros_settings_; - CrosSettingsProvider* device_settings_provider_; - StubCrosSettingsProvider stub_settings_provider_; + ScopedCrosSettingsTestHelper settings_helper_; scoped_ptr<ScopedTestingLocalState> local_state_; - ScopedTestDeviceSettingsService test_device_settings_service_; - ScopedTestCrosSettings test_cros_settings_; - scoped_ptr<ScopedUserManagerEnabler> user_manager_enabler_; base::ScopedTempDir temp_dir_; };
diff --git a/chrome/browser/chromeos/login/wizard_controller.cc b/chrome/browser/chromeos/login/wizard_controller.cc index 56f4a8a5..493fabb 100644 --- a/chrome/browser/chromeos/login/wizard_controller.cc +++ b/chrome/browser/chromeos/login/wizard_controller.cc
@@ -286,7 +286,13 @@ } ErrorScreen* WizardController::GetErrorScreen() { - return static_cast<ErrorScreen*>(GetScreen(kErrorScreenName)); + return oobe_display_->GetErrorScreen(); +} + +BaseScreen* WizardController::GetScreen(const std::string& screen_name) { + if (screen_name == kErrorScreenName) + return GetErrorScreen(); + return ScreenManager::GetScreen(screen_name); } BaseScreen* WizardController::CreateScreen(const std::string& screen_name) { @@ -295,13 +301,6 @@ new NetworkScreen(this, this, oobe_display_->GetNetworkView())); screen->Initialize(nullptr /* context */); return screen.release(); - } else if (screen_name == kErrorScreenName) { - scoped_ptr<ErrorScreen> screen = - static_cast<OobeUI*>(oobe_display_)->GetErrorScreen(); - if (!screen) - screen.reset(new ErrorScreen(this, oobe_display_->GetNetworkErrorView())); - screen->Initialize(nullptr /* context */); - return screen.release(); } else if (screen_name == kUpdateScreenName) { scoped_ptr<UpdateScreen> screen(new UpdateScreen( this, oobe_display_->GetUpdateView(), remora_controller_.get())); @@ -1054,11 +1053,12 @@ } } -void WizardController::ConfigureHost(bool accepted_eula, - const std::string& lang, - const std::string& timezone, - bool send_reports, - const std::string& keyboard_layout) { +void WizardController::ConfigureHostRequested( + bool accepted_eula, + const std::string& lang, + const std::string& timezone, + bool send_reports, + const std::string& keyboard_layout) { VLOG(1) << "ConfigureHost locale=" << lang << ", timezone=" << timezone << ", keyboard_layout=" << keyboard_layout; if (accepted_eula) // Always true. @@ -1071,6 +1071,11 @@ network_screen->SetInputMethod(keyboard_layout); } +void WizardController::AddNetworkRequested(const std::string& onc_spec) { + NetworkScreen* network_screen = NetworkScreen::Get(this); + network_screen->CreateNetworkFromOnc(onc_spec); +} + void WizardController::OnEnableDebuggingScreenRequested() { if (!login_screen_started()) AdvanceToScreen(WizardController::kEnableDebuggingScreenName);
diff --git a/chrome/browser/chromeos/login/wizard_controller.h b/chrome/browser/chromeos/login/wizard_controller.h index 0a4b8c6..87e79cff 100644 --- a/chrome/browser/chromeos/login/wizard_controller.h +++ b/chrome/browser/chromeos/login/wizard_controller.h
@@ -133,6 +133,7 @@ bool login_screen_started() const { return login_screen_started_; } // ScreenManager implementation. + BaseScreen* GetScreen(const std::string& screen_name) override; BaseScreen* CreateScreen(const std::string& screen_name) override; static const char kNetworkScreenName[]; @@ -248,11 +249,12 @@ void SetHostConfiguration() override; // Override from HostPairingScreen::Delegate: - void ConfigureHost(bool accepted_eula, - const std::string& lang, - const std::string& timezone, - bool send_reports, - const std::string& keyboard_layout) override; + void ConfigureHostRequested(bool accepted_eula, + const std::string& lang, + const std::string& timezone, + bool send_reports, + const std::string& keyboard_layout) override; + void AddNetworkRequested(const std::string& onc_spec) override; // Override from NetworkScreen::Delegate: void OnEnableDebuggingScreenRequested() override;
diff --git a/chrome/browser/chromeos/login/wizard_controller_browsertest.cc b/chrome/browser/chromeos/login/wizard_controller_browsertest.cc index 3b717469..1d6f75ad 100644 --- a/chrome/browser/chromeos/login/wizard_controller_browsertest.cc +++ b/chrome/browser/chromeos/login/wizard_controller_browsertest.cc
@@ -1012,11 +1012,6 @@ if (!GetParam()) local_state_dict.SetBoolean(prefs::kWebviewSigninDisabled, true); - // TODO(paulmeyer): Re-enable webview version of this test - // (drop this condition) once http://crbug.com/452452 is fixed. - if (GetParam()) - local_state_dict.SetBoolean(prefs::kWebviewSigninDisabled, true); - CHECK(JSONFileValueSerializer(local_state_path) .Serialize(local_state_dict)); } @@ -1044,9 +1039,11 @@ auth_needed_waiter.Wait(); } +// TODO(paulmeyer): Re-enable webview version of this test +// (drop this condition) once http://crbug.com/452452 is fixed. INSTANTIATE_TEST_CASE_P(WizardControllerProxyAuthOnSigninSuite, WizardControllerProxyAuthOnSigninTest, - testing::Bool()); + testing::Values(false)); class WizardControllerKioskFlowTest : public WizardControllerFlowTest { protected:
diff --git a/chrome/browser/chromeos/mobile/mobile_activator.cc b/chrome/browser/chromeos/mobile/mobile_activator.cc index 73b5d44..02b0b534 100644 --- a/chrome/browser/chromeos/mobile/mobile_activator.cc +++ b/chrome/browser/chromeos/mobile/mobile_activator.cc
@@ -239,17 +239,17 @@ } void MobileActivator::AddObserver(MobileActivator::Observer* observer) { - DCHECK(content::BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); observers_.AddObserver(observer); } void MobileActivator::RemoveObserver(MobileActivator::Observer* observer) { - DCHECK(content::BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); observers_.RemoveObserver(observer); } void MobileActivator::InitiateActivation(const std::string& service_path) { - DCHECK(content::BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); const NetworkState* network = GetNetworkState(service_path); if (!network) { LOG(WARNING) << "Cellular service can't be found: " << service_path;
diff --git a/chrome/browser/chromeos/net/network_portal_web_dialog.cc b/chrome/browser/chromeos/net/network_portal_web_dialog.cc index fd14550..3b6db22c 100644 --- a/chrome/browser/chromeos/net/network_portal_web_dialog.cc +++ b/chrome/browser/chromeos/net/network_portal_web_dialog.cc
@@ -94,8 +94,7 @@ void NetworkPortalWebDialog::OnCloseContents(content::WebContents* /* source */, bool* out_close_dialog) { - if (out_close_dialog) - *out_close_dialog = true; + *out_close_dialog = true; } bool NetworkPortalWebDialog::ShouldShowDialogTitle() const {
diff --git a/chrome/browser/chromeos/ownership/fake_owner_settings_service.cc b/chrome/browser/chromeos/ownership/fake_owner_settings_service.cc index b515447..8042251 100644 --- a/chrome/browser/chromeos/ownership/fake_owner_settings_service.cc +++ b/chrome/browser/chromeos/ownership/fake_owner_settings_service.cc
@@ -4,13 +4,27 @@ #include "chrome/browser/chromeos/ownership/fake_owner_settings_service.h" +#include "base/logging.h" +#include "chrome/browser/chromeos/settings/stub_cros_settings_provider.h" +#include "components/ownership/mock_owner_key_util.h" + namespace chromeos { +FakeOwnerSettingsService::FakeOwnerSettingsService(Profile* profile) + : OwnerSettingsServiceChromeOS(nullptr, + profile, + new ownership::MockOwnerKeyUtil()), + set_management_settings_result_(true), + settings_provider_(nullptr) { +} + FakeOwnerSettingsService::FakeOwnerSettingsService( Profile* profile, - const scoped_refptr<ownership::OwnerKeyUtil>& owner_key_util) + const scoped_refptr<ownership::OwnerKeyUtil>& owner_key_util, + StubCrosSettingsProvider* provider) : OwnerSettingsServiceChromeOS(nullptr, profile, owner_key_util), - set_management_settings_result_(true) { + set_management_settings_result_(true), + settings_provider_(provider) { } FakeOwnerSettingsService::~FakeOwnerSettingsService() { @@ -23,4 +37,11 @@ callback.Run(set_management_settings_result_); } +bool FakeOwnerSettingsService::Set(const std::string& setting, + const base::Value& value) { + CHECK(settings_provider_); + settings_provider_->Set(setting, value); + return true; +} + } // namespace chromeos
diff --git a/chrome/browser/chromeos/ownership/fake_owner_settings_service.h b/chrome/browser/chromeos/ownership/fake_owner_settings_service.h index bed4d06..3f785c1 100644 --- a/chrome/browser/chromeos/ownership/fake_owner_settings_service.h +++ b/chrome/browser/chromeos/ownership/fake_owner_settings_service.h
@@ -17,11 +17,15 @@ namespace chromeos { +class StubCrosSettingsProvider; + class FakeOwnerSettingsService : public OwnerSettingsServiceChromeOS { public: + explicit FakeOwnerSettingsService(Profile* profile); FakeOwnerSettingsService( Profile* profile, - const scoped_refptr<ownership::OwnerKeyUtil>& owner_key_util); + const scoped_refptr<ownership::OwnerKeyUtil>& owner_key_util, + StubCrosSettingsProvider* provider); ~FakeOwnerSettingsService() override; void set_set_management_settings_result(bool success) { @@ -36,10 +40,12 @@ void SetManagementSettings( const ManagementSettings& settings, const OnManagementSettingsSetCallback& callback) override; + bool Set(const std::string& setting, const base::Value& value) override; private: bool set_management_settings_result_; ManagementSettings last_settings_; + StubCrosSettingsProvider* settings_provider_; DISALLOW_COPY_AND_ASSIGN(FakeOwnerSettingsService); };
diff --git a/chrome/browser/chromeos/ownership/owner_settings_service_chromeos.cc b/chrome/browser/chromeos/ownership/owner_settings_service_chromeos.cc index bac0fba..a96f0aa 100644 --- a/chrome/browser/chromeos/ownership/owner_settings_service_chromeos.cc +++ b/chrome/browser/chromeos/ownership/owner_settings_service_chromeos.cc
@@ -174,6 +174,7 @@ NOTREACHED(); return false; } + } // namespace OwnerSettingsServiceChromeOS::ManagementSettings::ManagementSettings() {
diff --git a/chrome/browser/chromeos/policy/blocking_login_browsertest.cc b/chrome/browser/chromeos/policy/blocking_login_browsertest.cc index 03b0504..72db1d7 100644 --- a/chrome/browser/chromeos/policy/blocking_login_browsertest.cc +++ b/chrome/browser/chromeos/policy/blocking_login_browsertest.cc
@@ -85,7 +85,10 @@ public content::NotificationObserver, public testing::WithParamInterface<BlockingLoginTestParam> { public: - BlockingLoginTest() : profile_added_(NULL) {} + BlockingLoginTest() : profile_added_(NULL) { + // TODO(nkostylev): Fix this test for webview. http://crbug.com/477402 + set_use_webview(false); + } void SetUpCommandLine(base::CommandLine* command_line) override { OobeBaseTest::SetUpCommandLine(command_line);
diff --git a/chrome/browser/chromeos/policy/consumer_unenrollment_handler_unittest.cc b/chrome/browser/chromeos/policy/consumer_unenrollment_handler_unittest.cc index cacf3960..1a9ba8b 100644 --- a/chrome/browser/chromeos/policy/consumer_unenrollment_handler_unittest.cc +++ b/chrome/browser/chromeos/policy/consumer_unenrollment_handler_unittest.cc
@@ -59,9 +59,8 @@ base::ThreadTaskRunnerHandle::Get())); // Set up FakeOwnerSettingsService. - fake_owner_settings_service_.reset( - new chromeos::FakeOwnerSettingsService( - profile_.get(), owner_key_util_)); + fake_owner_settings_service_.reset(new chromeos::FakeOwnerSettingsService( + profile_.get(), owner_key_util_, nullptr)); chromeos::OwnerSettingsServiceChromeOS::ManagementSettings settings; settings.management_mode = policy::MANAGEMENT_MODE_CONSUMER_MANAGED; settings.request_token = "fake_request_token";
diff --git a/chrome/browser/chromeos/policy/device_local_account.cc b/chrome/browser/chromeos/policy/device_local_account.cc index 80b89b2..63c7a14 100644 --- a/chrome/browser/chromeos/policy/device_local_account.cc +++ b/chrome/browser/chromeos/policy/device_local_account.cc
@@ -12,6 +12,7 @@ #include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" #include "base/values.h" +#include "chrome/browser/chromeos/ownership/owner_settings_service_chromeos.h" #include "chrome/browser/chromeos/settings/cros_settings.h" #include "chromeos/login/user_names.h" #include "chromeos/settings/cros_settings_names.h" @@ -91,9 +92,8 @@ return true; } -void SetDeviceLocalAccounts( - chromeos::CrosSettings* cros_settings, - const std::vector<DeviceLocalAccount>& accounts) { +void SetDeviceLocalAccounts(chromeos::OwnerSettingsServiceChromeOS* service, + const std::vector<DeviceLocalAccount>& accounts) { base::ListValue list; for (std::vector<DeviceLocalAccount>::const_iterator it = accounts.begin(); it != accounts.end(); ++it) { @@ -117,7 +117,7 @@ list.Append(entry.release()); } - cros_settings->Set(chromeos::kAccountsPrefDeviceLocalAccounts, list); + service->Set(chromeos::kAccountsPrefDeviceLocalAccounts, list); } std::vector<DeviceLocalAccount> GetDeviceLocalAccounts(
diff --git a/chrome/browser/chromeos/policy/device_local_account.h b/chrome/browser/chromeos/policy/device_local_account.h index 69a7480..8827119 100644 --- a/chrome/browser/chromeos/policy/device_local_account.h +++ b/chrome/browser/chromeos/policy/device_local_account.h
@@ -10,6 +10,7 @@ namespace chromeos { class CrosSettings; +class OwnerSettingsServiceChromeOS; } namespace policy { @@ -64,12 +65,11 @@ bool IsDeviceLocalAccountUser(const std::string& user_id, DeviceLocalAccount::Type* type); -// Stores a list of device-local accounts in |cros_settings|. The accounts are -// stored as a list of dictionaries with each dictionary containing the -// information about one |DeviceLocalAccount|. -void SetDeviceLocalAccounts( - chromeos::CrosSettings* cros_settings, - const std::vector<DeviceLocalAccount>& accounts); +// Stores a list of device-local accounts in |service|. The accounts are stored +// as a list of dictionaries with each dictionary containing the information +// about one |DeviceLocalAccount|. +void SetDeviceLocalAccounts(chromeos::OwnerSettingsServiceChromeOS* service, + const std::vector<DeviceLocalAccount>& accounts); // Retrieves a list of device-local accounts from |cros_settings|. std::vector<DeviceLocalAccount> GetDeviceLocalAccounts(
diff --git a/chrome/browser/chromeos/policy/device_policy_cros_browser_test.cc b/chrome/browser/chromeos/policy/device_policy_cros_browser_test.cc index 59e8401..8e78071 100644 --- a/chrome/browser/chromeos/policy/device_policy_cros_browser_test.cc +++ b/chrome/browser/chromeos/policy/device_policy_cros_browser_test.cc
@@ -4,7 +4,6 @@ #include "chrome/browser/chromeos/policy/device_policy_cros_browser_test.h" -#include <string> #include <vector> #include "base/files/file_path.h" @@ -31,13 +30,14 @@ DevicePolicyCrosTestHelper::~DevicePolicyCrosTestHelper() {} -void DevicePolicyCrosTestHelper::MarkAsEnterpriseOwned() { +// static +void DevicePolicyCrosTestHelper::MarkAsEnterpriseOwnedBy( + const std::string& user_name) { OverridePaths(); const std::string install_attrs_blob( EnterpriseInstallAttributes:: - GetEnterpriseOwnedInstallAttributesBlobForTesting( - device_policy_.policy_data().username())); + GetEnterpriseOwnedInstallAttributesBlobForTesting(user_name)); base::FilePath install_attrs_file; ASSERT_TRUE( @@ -48,6 +48,10 @@ install_attrs_blob.size())); } +void DevicePolicyCrosTestHelper::MarkAsEnterpriseOwned() { + MarkAsEnterpriseOwnedBy(device_policy_.policy_data().username()); +} + void DevicePolicyCrosTestHelper::InstallOwnerKey() { OverridePaths(); @@ -63,6 +67,7 @@ static_cast<int>(owner_key_bits.size())); } +// static void DevicePolicyCrosTestHelper::OverridePaths() { // This is usually done by ChromeBrowserMainChromeOS, but some tests // use the overridden paths before ChromeBrowserMain starts. Make sure that
diff --git a/chrome/browser/chromeos/policy/device_policy_cros_browser_test.h b/chrome/browser/chromeos/policy/device_policy_cros_browser_test.h index e6e90a9e..ca528977 100644 --- a/chrome/browser/chromeos/policy/device_policy_cros_browser_test.h +++ b/chrome/browser/chromeos/policy/device_policy_cros_browser_test.h
@@ -5,6 +5,8 @@ #ifndef CHROME_BROWSER_CHROMEOS_POLICY_DEVICE_POLICY_CROS_BROWSER_TEST_H_ #define CHROME_BROWSER_CHROMEOS_POLICY_DEVICE_POLICY_CROS_BROWSER_TEST_H_ +#include <string> + #include "base/basictypes.h" #include "base/compiler_specific.h" #include "chrome/browser/chromeos/policy/device_policy_builder.h" @@ -25,6 +27,7 @@ // Marks the device as enterprise-owned. Must be called to make device // policies apply Chrome-wide. If this is not called, device policies will // affect CrosSettings only. + static void MarkAsEnterpriseOwnedBy(const std::string& user_name); void MarkAsEnterpriseOwned(); // Writes the owner key to disk. To be called before installing a policy. @@ -33,7 +36,7 @@ DevicePolicyBuilder* device_policy() { return &device_policy_; } private: - void OverridePaths(); + static void OverridePaths(); // Carries Chrome OS device policies for tests. DevicePolicyBuilder device_policy_;
diff --git a/chrome/browser/chromeos/policy/device_status_collector_browsertest.cc b/chrome/browser/chromeos/policy/device_status_collector_browsertest.cc index c031b59..dbd79a7 100644 --- a/chrome/browser/chromeos/policy/device_status_collector_browsertest.cc +++ b/chrome/browser/chromeos/policy/device_status_collector_browsertest.cc
@@ -18,12 +18,11 @@ #include "base/threading/sequenced_worker_pool.h" #include "chrome/browser/chromeos/login/users/mock_user_manager.h" #include "chrome/browser/chromeos/login/users/scoped_user_manager_enabler.h" +#include "chrome/browser/chromeos/ownership/fake_owner_settings_service.h" #include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h" #include "chrome/browser/chromeos/policy/device_local_account.h" #include "chrome/browser/chromeos/policy/stub_enterprise_install_attributes.h" -#include "chrome/browser/chromeos/settings/cros_settings.h" -#include "chrome/browser/chromeos/settings/device_settings_service.h" -#include "chrome/browser/chromeos/settings/stub_cros_settings_provider.h" +#include "chrome/browser/chromeos/settings/scoped_cros_settings_test_helper.h" #include "chrome/common/pref_names.h" #include "chrome/test/base/testing_browser_process.h" #include "chromeos/dbus/cros_disks_client.h" @@ -37,7 +36,6 @@ #include "chromeos/network/network_state.h" #include "chromeos/network/network_state_handler.h" #include "chromeos/settings/cros_settings_names.h" -#include "chromeos/settings/cros_settings_provider.h" #include "chromeos/system/fake_statistics_provider.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/geolocation_provider.h" @@ -230,20 +228,20 @@ class DeviceStatusCollectorTest : public testing::Test { public: DeviceStatusCollectorTest() - : ui_thread_(content::BrowserThread::UI, &message_loop_), - file_thread_(content::BrowserThread::FILE, &message_loop_), - io_thread_(content::BrowserThread::IO, &message_loop_), - install_attributes_("managed.com", - "user@managed.com", - "device_id", - DEVICE_MODE_ENTERPRISE), - user_manager_(new chromeos::MockUserManager()), - user_manager_enabler_(user_manager_), - fake_device_local_account_( - policy::DeviceLocalAccount::TYPE_KIOSK_APP, - kKioskAccountId, - kKioskAppId, - std::string() /* kiosk_app_update_url */) { + : ui_thread_(content::BrowserThread::UI, &message_loop_), + file_thread_(content::BrowserThread::FILE, &message_loop_), + io_thread_(content::BrowserThread::IO, &message_loop_), + install_attributes_("managed.com", + "user@managed.com", + "device_id", + DEVICE_MODE_ENTERPRISE), + settings_helper_(false), + user_manager_(new chromeos::MockUserManager()), + user_manager_enabler_(user_manager_), + fake_device_local_account_(policy::DeviceLocalAccount::TYPE_KIOSK_APP, + kKioskAccountId, + kKioskAppId, + std::string() /* kiosk_app_update_url */) { // Run this test with a well-known timezone so that Time::LocalMidnight() // returns the same values on all machines. scoped_ptr<base::Environment> env(base::Environment::Create()); @@ -274,14 +272,9 @@ DiskMountManager::InitializeForTesting(mock_disk_mount_manager.release()); TestingDeviceStatusCollector::RegisterPrefs(prefs_.registry()); - // Remove the real DeviceSettingsProvider and replace it with a stub. - cros_settings_ = chromeos::CrosSettings::Get(); - device_settings_provider_ = - cros_settings_->GetProvider(chromeos::kReportDeviceVersionInfo); - EXPECT_TRUE(device_settings_provider_ != NULL); - EXPECT_TRUE( - cros_settings_->RemoveSettingsProvider(device_settings_provider_)); - cros_settings_->AddSettingsProvider(&stub_settings_provider_); + settings_helper_.ReplaceProvider(chromeos::kReportDeviceActivityTimes); + owner_settings_service_ = + settings_helper_.CreateOwnerSettingsService(nullptr); RestartStatusCollector(base::Bind(&GetEmptyVolumeInfo), base::Bind(&GetEmptyCPUStatistics)); @@ -301,18 +294,16 @@ message_loop_.RunUntilIdle(); storage::ExternalMountPoints::GetSystemInstance()->RevokeAllFileSystems(); DiskMountManager::Shutdown(); - - // Restore the real DeviceSettingsProvider. - EXPECT_TRUE( - cros_settings_->RemoveSettingsProvider(&stub_settings_provider_)); - cros_settings_->AddSettingsProvider(device_settings_provider_); } void SetUp() override { // Disable network interface reporting since it requires additional setup. - cros_settings_->SetBoolean(chromeos::kReportDeviceNetworkInterfaces, false); + settings_helper_.SetBoolean(chromeos::kReportDeviceNetworkInterfaces, + false); } + void TearDown() override { settings_helper_.RestoreProvider(); } + void RestartStatusCollector( const policy::DeviceStatusCollector::VolumeInfoFetcher& volume_info, const policy::DeviceStatusCollector::CPUStatisticsFetcher& cpu_stats) { @@ -370,7 +361,7 @@ void MockRunningKioskApp(const DeviceLocalAccount& account) { std::vector<DeviceLocalAccount> accounts; accounts.push_back(account); - SetDeviceLocalAccounts(cros_settings_, accounts); + SetDeviceLocalAccounts(owner_settings_service_.get(), accounts); user_manager_->CreateKioskAppUser(account.user_id); EXPECT_CALL(*user_manager_, IsLoggedInAsKioskApp()).WillRepeatedly( Return(true)); @@ -396,9 +387,8 @@ DiskMountManager::MountPointMap mount_point_map_; chromeos::ScopedTestDeviceSettingsService test_device_settings_service_; chromeos::ScopedTestCrosSettings test_cros_settings_; - chromeos::CrosSettings* cros_settings_; - chromeos::CrosSettingsProvider* device_settings_provider_; - chromeos::StubCrosSettingsProvider stub_settings_provider_; + chromeos::ScopedCrosSettingsTestHelper settings_helper_; + scoped_ptr<chromeos::FakeOwnerSettingsService> owner_settings_service_; chromeos::MockUserManager* user_manager_; chromeos::ScopedUserManagerEnabler user_manager_enabler_; em::DeviceStatusReportRequest status_; @@ -412,7 +402,7 @@ ui::IDLE_STATE_IDLE, ui::IDLE_STATE_IDLE }; - cros_settings_->SetBoolean(chromeos::kReportDeviceActivityTimes, true); + settings_helper_.SetBoolean(chromeos::kReportDeviceActivityTimes, true); // Test reporting with no data. GetStatus(); @@ -439,7 +429,7 @@ ui::IDLE_STATE_ACTIVE, ui::IDLE_STATE_ACTIVE }; - cros_settings_->SetBoolean(chromeos::kReportDeviceActivityTimes, true); + settings_helper_.SetBoolean(chromeos::kReportDeviceActivityTimes, true); // Test a single active sample. status_collector_->Simulate(test_states, 1); @@ -466,7 +456,7 @@ ui::IDLE_STATE_IDLE, ui::IDLE_STATE_ACTIVE }; - cros_settings_->SetBoolean(chromeos::kReportDeviceActivityTimes, true); + settings_helper_.SetBoolean(chromeos::kReportDeviceActivityTimes, true); status_collector_->Simulate(test_states, sizeof(test_states) / sizeof(ui::IdleState)); GetStatus(); @@ -482,7 +472,7 @@ ui::IDLE_STATE_IDLE, ui::IDLE_STATE_IDLE }; - cros_settings_->SetBoolean(chromeos::kReportDeviceActivityTimes, true); + settings_helper_.SetBoolean(chromeos::kReportDeviceActivityTimes, true); status_collector_->Simulate(test_states, sizeof(test_states) / sizeof(ui::IdleState)); @@ -507,7 +497,7 @@ ui::IDLE_STATE_IDLE, ui::IDLE_STATE_IDLE }; - cros_settings_->SetBoolean(chromeos::kReportDeviceActivityTimes, true); + settings_helper_.SetBoolean(chromeos::kReportDeviceActivityTimes, true); status_collector_->Simulate(test_states, sizeof(test_states) / sizeof(ui::IdleState)); GetStatus(); @@ -521,7 +511,7 @@ }; const int kMaxDays = 10; - cros_settings_->SetBoolean(chromeos::kReportDeviceActivityTimes, true); + settings_helper_.SetBoolean(chromeos::kReportDeviceActivityTimes, true); status_collector_->set_max_stored_past_activity_days(kMaxDays - 1); status_collector_->set_max_stored_future_activity_days(1); Time baseline = Time::Now().LocalMidnight(); @@ -576,7 +566,7 @@ TEST_F(DeviceStatusCollectorTest, ActivityTimesOff) { // Device activity times should not be reported if explicitly disabled. - cros_settings_->SetBoolean(chromeos::kReportDeviceActivityTimes, false); + settings_helper_.SetBoolean(chromeos::kReportDeviceActivityTimes, false); ui::IdleState test_states[] = { ui::IDLE_STATE_ACTIVE, @@ -594,7 +584,7 @@ ui::IdleState test_states[] = { ui::IDLE_STATE_ACTIVE }; - cros_settings_->SetBoolean(chromeos::kReportDeviceActivityTimes, true); + settings_helper_.SetBoolean(chromeos::kReportDeviceActivityTimes, true); // Set the baseline time to 10 seconds after midnight. status_collector_->SetBaselineTime( @@ -626,7 +616,7 @@ ui::IDLE_STATE_ACTIVE, ui::IDLE_STATE_ACTIVE, }; - cros_settings_->SetBoolean(chromeos::kReportDeviceActivityTimes, true); + settings_helper_.SetBoolean(chromeos::kReportDeviceActivityTimes, true); status_collector_->Simulate(test_states, 2); GetStatus(); @@ -654,14 +644,14 @@ EXPECT_EQ("Verified", status_.boot_mode()); // Test that boot mode data is not reported if the pref turned off. - cros_settings_->SetBoolean(chromeos::kReportDeviceBootMode, false); + settings_helper_.SetBoolean(chromeos::kReportDeviceBootMode, false); GetStatus(); EXPECT_FALSE(status_.has_boot_mode()); // Turn the pref on, and check that the status is reported iff the // statistics provider returns valid data. - cros_settings_->SetBoolean(chromeos::kReportDeviceBootMode, true); + settings_helper_.SetBoolean(chromeos::kReportDeviceBootMode, true); fake_statistics_provider_.SetMachineStatistic( chromeos::system::kDevSwitchBootKey, "(error)"); @@ -695,13 +685,13 @@ // When the pref to collect this data is not enabled, expect that none of // the fields are present in the protobuf. - cros_settings_->SetBoolean(chromeos::kReportDeviceVersionInfo, false); + settings_helper_.SetBoolean(chromeos::kReportDeviceVersionInfo, false); GetStatus(); EXPECT_FALSE(status_.has_browser_version()); EXPECT_FALSE(status_.has_os_version()); EXPECT_FALSE(status_.has_firmware_version()); - cros_settings_->SetBoolean(chromeos::kReportDeviceVersionInfo, true); + settings_helper_.SetBoolean(chromeos::kReportDeviceVersionInfo, true); GetStatus(); EXPECT_TRUE(status_.has_browser_version()); EXPECT_TRUE(status_.has_os_version()); @@ -733,7 +723,7 @@ // Check that when device location reporting is enabled and a valid fix is // available, the location is reported and is stored in local state. SetMockPositionToReturnNext(valid_fix); - cros_settings_->SetBoolean(chromeos::kReportDeviceLocation, true); + settings_helper_.SetBoolean(chromeos::kReportDeviceLocation, true); EXPECT_FALSE(prefs_.GetDictionary(prefs::kDeviceLocation)->empty()); CheckThatAValidLocationIsReported(); @@ -748,7 +738,7 @@ // Check that after disabling location reporting again, the last known // location has been cleared from local state and is no longer reported. SetMockPositionToReturnNext(valid_fix); - cros_settings_->SetBoolean(chromeos::kReportDeviceLocation, false); + settings_helper_.SetBoolean(chromeos::kReportDeviceLocation, false); // Allow the new pref to propagate to the status collector. message_loop_.RunUntilIdle(); EXPECT_TRUE(prefs_.GetDictionary(prefs::kDeviceLocation)->empty()); @@ -757,7 +747,7 @@ // Check that after enabling location reporting again, an error is reported // if no valid fix is available. SetMockPositionToReturnNext(invalid_fix); - cros_settings_->SetBoolean(chromeos::kReportDeviceLocation, true); + settings_helper_.SetBoolean(chromeos::kReportDeviceLocation, true); // Allow the new pref to propagate to the status collector. message_loop_.RunUntilIdle(); CheckThatALocationErrorIsReported(); @@ -777,7 +767,7 @@ EXPECT_EQ(6, status_.user_size()); // Verify that users are reported after enabling the setting. - cros_settings_->SetBoolean(chromeos::kReportDeviceUsers, true); + settings_helper_.SetBoolean(chromeos::kReportDeviceUsers, true); GetStatus(); EXPECT_EQ(6, status_.user_size()); EXPECT_EQ(em::DeviceUser::USER_TYPE_MANAGED, status_.user(0).type()); @@ -794,7 +784,7 @@ EXPECT_EQ("user5@managed.com", status_.user(5).email()); // Verify that users are no longer reported if setting is disabled. - cros_settings_->SetBoolean(chromeos::kReportDeviceUsers, false); + settings_helper_.SetBoolean(chromeos::kReportDeviceUsers, false); GetStatus(); EXPECT_EQ(0, status_.user_size()); } @@ -843,7 +833,7 @@ } // Now turn off hardware status reporting - should have no data. - cros_settings_->SetBoolean(chromeos::kReportDeviceHardwareStatus, false); + settings_helper_.SetBoolean(chromeos::kReportDeviceHardwareStatus, false); GetStatus(); EXPECT_EQ(0, status_.volume_info_size()); } @@ -903,21 +893,21 @@ EXPECT_EQ(0, utilization); // Turning off hardware reporting should not report CPU utilization. - cros_settings_->SetBoolean(chromeos::kReportDeviceHardwareStatus, false); + settings_helper_.SetBoolean(chromeos::kReportDeviceHardwareStatus, false); GetStatus(); EXPECT_EQ(0, status_.cpu_utilization_pct().size()); } TEST_F(DeviceStatusCollectorTest, NoSessionStatusIfNotKioskMode) { // Should not report session status if we don't have an active kiosk app. - cros_settings_->SetBoolean(chromeos::kReportDeviceSessionStatus, true); + settings_helper_.SetBoolean(chromeos::kReportDeviceSessionStatus, true); em::SessionStatusReportRequest session_status; EXPECT_FALSE(status_collector_->GetDeviceSessionStatus(&session_status)); } TEST_F(DeviceStatusCollectorTest, NoSessionStatusIfSessionReportingDisabled) { // Should not report session status if session status reporting is disabled. - cros_settings_->SetBoolean(chromeos::kReportDeviceSessionStatus, false); + settings_helper_.SetBoolean(chromeos::kReportDeviceSessionStatus, false); status_collector_->set_kiosk_account(make_scoped_ptr( new policy::DeviceLocalAccount(fake_device_local_account_)).Pass()); // Set up a device-local account for single-app kiosk mode. @@ -928,7 +918,7 @@ } TEST_F(DeviceStatusCollectorTest, ReportSessionStatus) { - cros_settings_->SetBoolean(chromeos::kReportDeviceSessionStatus, true); + settings_helper_.SetBoolean(chromeos::kReportDeviceSessionStatus, true); status_collector_->set_kiosk_account(make_scoped_ptr( new policy::DeviceLocalAccount(fake_device_local_account_)).Pass()); @@ -1164,13 +1154,13 @@ EXPECT_LT(0, status_.network_state_size()); // No interfaces should be reported if the policy is off. - cros_settings_->SetBoolean(chromeos::kReportDeviceNetworkInterfaces, false); + settings_helper_.SetBoolean(chromeos::kReportDeviceNetworkInterfaces, false); GetStatus(); EXPECT_EQ(0, status_.network_interface_size()); EXPECT_EQ(0, status_.network_state_size()); // Switch the policy on and verify the interface list is present. - cros_settings_->SetBoolean(chromeos::kReportDeviceNetworkInterfaces, true); + settings_helper_.SetBoolean(chromeos::kReportDeviceNetworkInterfaces, true); GetStatus(); int count = 0;
diff --git a/chrome/browser/chromeos/policy/extension_cache_unittest.cc b/chrome/browser/chromeos/policy/extension_cache_unittest.cc index 0b88894..5191e0c 100644 --- a/chrome/browser/chromeos/policy/extension_cache_unittest.cc +++ b/chrome/browser/chromeos/policy/extension_cache_unittest.cc
@@ -10,14 +10,11 @@ #include "base/memory/scoped_ptr.h" #include "base/run_loop.h" #include "base/time/time.h" -#include "chrome/browser/chromeos/settings/cros_settings.h" -#include "chrome/browser/chromeos/settings/device_settings_service.h" -#include "chrome/browser/chromeos/settings/stub_cros_settings_provider.h" +#include "chrome/browser/chromeos/settings/scoped_cros_settings_test_helper.h" #include "chrome/browser/extensions/updater/chromeos_extension_cache_delegate.h" #include "chrome/browser/extensions/updater/extension_cache_impl.h" #include "chrome/browser/extensions/updater/local_extension_cache.h" #include "chromeos/settings/cros_settings_names.h" -#include "chromeos/settings/cros_settings_provider.h" #include "content/public/test/test_browser_thread_bundle.h" #include "testing/gtest/include/gtest/gtest.h" @@ -58,39 +55,14 @@ } // namespace class ExtensionCacheTest : public testing::Test { - public: - void SetUp() override { - // Swap out the DeviceSettingsProvider with our stub settings provider - // so we can set values for maximum extension cache size. - chromeos::CrosSettings* const cros_settings = chromeos::CrosSettings::Get(); - device_settings_provider_ = - cros_settings->GetProvider(chromeos::kExtensionCacheSize); - EXPECT_TRUE(device_settings_provider_); - EXPECT_TRUE( - cros_settings->RemoveSettingsProvider(device_settings_provider_)); - cros_settings->AddSettingsProvider(&stub_settings_provider_); - } - - void TearDown() override { - // Restore the real DeviceSettingsProvider. - chromeos::CrosSettings* const cros_settings = chromeos::CrosSettings::Get(); - EXPECT_TRUE( - cros_settings->RemoveSettingsProvider(&stub_settings_provider_)); - cros_settings->AddSettingsProvider(device_settings_provider_); - } - - // Helpers used to mock out cros settings. - chromeos::ScopedTestDeviceSettingsService test_device_settings_service_; - chromeos::ScopedTestCrosSettings test_cros_settings_; - chromeos::CrosSettingsProvider* device_settings_provider_ = nullptr; - chromeos::StubCrosSettingsProvider stub_settings_provider_; - + protected: content::TestBrowserThreadBundle thread_bundle_; + chromeos::ScopedCrosSettingsTestHelper settings_helper_; }; TEST_F(ExtensionCacheTest, SizePolicy) { - chromeos::CrosSettings::Get()->SetInteger(chromeos::kExtensionCacheSize, - kMaxCacheSize); + settings_helper_.ReplaceProvider(chromeos::kExtensionCacheSize); + settings_helper_.SetInteger(chromeos::kExtensionCacheSize, kMaxCacheSize); // Create and initialize local cache. const base::Time now = base::Time::Now();
diff --git a/chrome/browser/chromeos/policy/heartbeat_scheduler_unittest.cc b/chrome/browser/chromeos/policy/heartbeat_scheduler_unittest.cc index 7a1a05c..8e5686b4e 100644 --- a/chrome/browser/chromeos/policy/heartbeat_scheduler_unittest.cc +++ b/chrome/browser/chromeos/policy/heartbeat_scheduler_unittest.cc
@@ -6,9 +6,7 @@ #include "base/strings/string_number_conversions.h" #include "base/test/test_simple_task_runner.h" -#include "chrome/browser/chromeos/settings/cros_settings.h" -#include "chrome/browser/chromeos/settings/device_settings_service.h" -#include "chrome/browser/chromeos/settings/stub_cros_settings_provider.h" +#include "chrome/browser/chromeos/settings/scoped_cros_settings_test_helper.h" #include "chromeos/settings/cros_settings_names.h" #include "components/gcm_driver/fake_gcm_driver.h" #include "content/public/test/test_utils.h" @@ -63,24 +61,11 @@ } void SetUp() override { - // Swap out the DeviceSettingsProvider with our stub settings provider - // so we can set values for the heartbeat frequency. - chromeos::CrosSettings* cros_settings = chromeos::CrosSettings::Get(); - device_settings_provider_ = - cros_settings->GetProvider(chromeos::kReportDeviceVersionInfo); - EXPECT_TRUE(device_settings_provider_); - EXPECT_TRUE( - cros_settings->RemoveSettingsProvider(device_settings_provider_)); - cros_settings->AddSettingsProvider(&stub_settings_provider_); + settings_helper_.ReplaceProvider(chromeos::kHeartbeatEnabled); } void TearDown() override { content::RunAllBlockingPoolTasksUntilIdle(); - // Restore the real DeviceSettingsProvider. - chromeos::CrosSettings* cros_settings = chromeos::CrosSettings::Get(); - EXPECT_TRUE(cros_settings->RemoveSettingsProvider( - &stub_settings_provider_)); - cros_settings->AddSettingsProvider(device_settings_provider_); } void CheckPendingTaskDelay(base::Time last_heartbeat, @@ -106,14 +91,8 @@ } base::MessageLoop loop_; - - // Helpers used to mock out cros settings. - chromeos::ScopedTestDeviceSettingsService test_device_settings_service_; - chromeos::ScopedTestCrosSettings test_cros_settings_; - chromeos::CrosSettingsProvider* device_settings_provider_; - chromeos::StubCrosSettingsProvider stub_settings_provider_; - MockGCMDriver gcm_driver_; + chromeos::ScopedCrosSettingsTestHelper settings_helper_; // TaskRunner used to run individual tests. scoped_refptr<base::TestSimpleTaskRunner> task_runner_; @@ -125,16 +104,14 @@ TEST_F(HeartbeatSchedulerTest, Basic) { // Just makes sure we can spin up and shutdown the scheduler with // heartbeats disabled. - chromeos::CrosSettings::Get()->SetBoolean( - chromeos::kHeartbeatEnabled, false); + settings_helper_.SetBoolean(chromeos::kHeartbeatEnabled, false); ASSERT_TRUE(task_runner_->GetPendingTasks().empty()); } TEST_F(HeartbeatSchedulerTest, PermanentlyFailedGCMRegistration) { // If heartbeats are enabled, we should register with GCMDriver. EXPECT_CALL(gcm_driver_, RegisterImpl(kHeartbeatGCMAppID, _)); - chromeos::CrosSettings::Get()->SetBoolean( - chromeos::kHeartbeatEnabled, true); + settings_helper_.SetBoolean(chromeos::kHeartbeatEnabled, true); gcm_driver_.CompleteRegistration( kHeartbeatGCMAppID, gcm::GCMClient::GCM_DISABLED); @@ -144,8 +121,7 @@ TEST_F(HeartbeatSchedulerTest, TemporarilyFailedGCMRegistration) { EXPECT_CALL(gcm_driver_, RegisterImpl(kHeartbeatGCMAppID, _)); - chromeos::CrosSettings::Get()->SetBoolean( - chromeos::kHeartbeatEnabled, true); + settings_helper_.SetBoolean(chromeos::kHeartbeatEnabled, true); gcm_driver_.CompleteRegistration( kHeartbeatGCMAppID, gcm::GCMClient::SERVER_ERROR); testing::Mock::VerifyAndClearExpectations(&gcm_driver_); @@ -165,8 +141,7 @@ TEST_F(HeartbeatSchedulerTest, ChangeHeartbeatFrequency) { EXPECT_CALL(gcm_driver_, RegisterImpl(kHeartbeatGCMAppID, _)); - chromeos::CrosSettings::Get()->SetBoolean( - chromeos::kHeartbeatEnabled, true); + settings_helper_.SetBoolean(chromeos::kHeartbeatEnabled, true); gcm_driver_.CompleteRegistration( kHeartbeatGCMAppID, gcm::GCMClient::SUCCESS); @@ -176,8 +151,7 @@ testing::Mock::VerifyAndClearExpectations(&gcm_driver_); const int new_delay = 1234*1000; // 1234 seconds. - chromeos::CrosSettings::Get()->SetInteger(chromeos::kHeartbeatFrequency, - new_delay); + settings_helper_.SetInteger(chromeos::kHeartbeatFrequency, new_delay); // Now run pending heartbeat task, should send a heartbeat. gcm::GCMClient::OutgoingMessage message; EXPECT_CALL(gcm_driver_, SendImpl(kHeartbeatGCMAppID, _, _)) @@ -197,8 +171,7 @@ TEST_F(HeartbeatSchedulerTest, DisableHeartbeats) { // Makes sure that we can disable heartbeats on the fly. EXPECT_CALL(gcm_driver_, RegisterImpl(kHeartbeatGCMAppID, _)); - chromeos::CrosSettings::Get()->SetBoolean( - chromeos::kHeartbeatEnabled, true); + settings_helper_.SetBoolean(chromeos::kHeartbeatEnabled, true); gcm::GCMClient::OutgoingMessage message; EXPECT_CALL(gcm_driver_, SendImpl(kHeartbeatGCMAppID, _, _)) .WillOnce(SaveArg<2>(&message)); @@ -221,8 +194,7 @@ testing::Mock::VerifyAndClearExpectations(&gcm_driver_); // Now disable heartbeats. Should get no more heartbeats sent. - chromeos::CrosSettings::Get()->SetBoolean( - chromeos::kHeartbeatEnabled, false); + settings_helper_.SetBoolean(chromeos::kHeartbeatEnabled, false); task_runner_->RunPendingTasks(); EXPECT_TRUE(task_runner_->GetPendingTasks().empty()); } @@ -232,8 +204,7 @@ EXPECT_CALL(gcm_driver_, RegisterImpl(kHeartbeatGCMAppID, _)); EXPECT_CALL(gcm_driver_, SendImpl(kHeartbeatGCMAppID, _, _)) .WillOnce(SaveArg<2>(&message)); - chromeos::CrosSettings::Get()->SetBoolean( - chromeos::kHeartbeatEnabled, true); + settings_helper_.SetBoolean(chromeos::kHeartbeatEnabled, true); gcm_driver_.CompleteRegistration( kHeartbeatGCMAppID, gcm::GCMClient::SUCCESS); task_runner_->RunPendingTasks();
diff --git a/chrome/browser/chromeos/policy/login_policy_test_base.cc b/chrome/browser/chromeos/policy/login_policy_test_base.cc index 5525ccf..710b0bda 100644 --- a/chrome/browser/chromeos/policy/login_policy_test_base.cc +++ b/chrome/browser/chromeos/policy/login_policy_test_base.cc
@@ -66,6 +66,8 @@ const char LoginPolicyTestBase::kAccountId[] = "user@example.com"; LoginPolicyTestBase::LoginPolicyTestBase() { + // TODO(nkostylev): Fix this test harness for webview. http://crbug.com/477402 + set_use_webview(false); set_open_about_blank_on_browser_launch(false); }
diff --git a/chrome/browser/chromeos/policy/network_configuration_updater_unittest.cc b/chrome/browser/chromeos/policy/network_configuration_updater_unittest.cc index 1dd6406..da4a95c 100644 --- a/chrome/browser/chromeos/policy/network_configuration_updater_unittest.cc +++ b/chrome/browser/chromeos/policy/network_configuration_updater_unittest.cc
@@ -12,8 +12,7 @@ #include "chrome/browser/chromeos/policy/device_network_configuration_updater.h" #include "chrome/browser/chromeos/policy/user_network_configuration_updater.h" #include "chrome/browser/chromeos/settings/cros_settings.h" -#include "chrome/browser/chromeos/settings/device_settings_service.h" -#include "chrome/browser/chromeos/settings/stub_cros_settings_provider.h" +#include "chrome/browser/chromeos/settings/scoped_cros_settings_test_helper.h" #include "chrome/test/base/testing_profile.h" #include "chromeos/network/fake_network_device_handler.h" #include "chromeos/network/mock_managed_network_configuration_handler.h" @@ -287,10 +286,7 @@ StrictMock<chromeos::MockManagedNetworkConfigurationHandler> network_config_handler_; FakeNetworkDeviceHandler network_device_handler_; - - // Not used directly. Required for CrosSettings. - chromeos::ScopedTestDeviceSettingsService scoped_device_settings_service_; - chromeos::ScopedTestCrosSettings scoped_cros_settings_; + chromeos::ScopedCrosSettingsTestHelper settings_helper_; // Ownership of certificate_importer_owned_ is passed to the // NetworkConfigurationUpdater. When that happens, |certificate_importer_| @@ -313,28 +309,16 @@ // Ignore network config updates. EXPECT_CALL(network_config_handler_, SetPolicy(_, _, _, _)).Times(AtLeast(1)); - // Setup the DataRoaming device setting. - chromeos::CrosSettings* cros_settings = chromeos::CrosSettings::Get(); - chromeos::CrosSettingsProvider* device_settings_provider = - cros_settings->GetProvider(chromeos::kSignedDataRoamingEnabled); - cros_settings->RemoveSettingsProvider(device_settings_provider); - delete device_settings_provider; - chromeos::StubCrosSettingsProvider* stub_settings_provider = - new chromeos::StubCrosSettingsProvider; - cros_settings->AddSettingsProvider(stub_settings_provider); - - chromeos::CrosSettings::Get()->Set(chromeos::kSignedDataRoamingEnabled, - base::FundamentalValue(false)); + settings_helper_.ReplaceProvider(chromeos::kSignedDataRoamingEnabled); + settings_helper_.SetBoolean(chromeos::kSignedDataRoamingEnabled, false); EXPECT_FALSE(network_device_handler_.allow_roaming_); CreateNetworkConfigurationUpdaterForDevicePolicy(); MarkPolicyProviderInitialized(); - chromeos::CrosSettings::Get()->Set(chromeos::kSignedDataRoamingEnabled, - base::FundamentalValue(true)); + settings_helper_.SetBoolean(chromeos::kSignedDataRoamingEnabled, true); EXPECT_TRUE(network_device_handler_.allow_roaming_); - chromeos::CrosSettings::Get()->Set(chromeos::kSignedDataRoamingEnabled, - base::FundamentalValue(false)); + settings_helper_.SetBoolean(chromeos::kSignedDataRoamingEnabled, false); EXPECT_FALSE(network_device_handler_.allow_roaming_); }
diff --git a/chrome/browser/chromeos/policy/policy_cert_verifier.cc b/chrome/browser/chromeos/policy/policy_cert_verifier.cc index 524ca947..cca86b06 100644 --- a/chrome/browser/chromeos/policy/policy_cert_verifier.cc +++ b/chrome/browser/chromeos/policy/policy_cert_verifier.cc
@@ -18,7 +18,7 @@ void MaybeSignalAnchorUse(int error, const base::Closure& anchor_used_callback, const net::CertVerifyResult& verify_result) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); if (error != net::OK || !verify_result.is_issued_by_additional_trust_anchor || anchor_used_callback.is_null()) { return; @@ -31,7 +31,7 @@ const net::CompletionCallback& completion_callback, const net::CertVerifyResult* verify_result, int error) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); MaybeSignalAnchorUse(error, anchor_used_callback, *verify_result); if (!completion_callback.is_null()) completion_callback.Run(error); @@ -42,7 +42,7 @@ PolicyCertVerifier::PolicyCertVerifier( const base::Closure& anchor_used_callback) : anchor_used_callback_(anchor_used_callback) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); } PolicyCertVerifier::~PolicyCertVerifier() {
diff --git a/chrome/browser/chromeos/policy/restore_on_startup_browsertest_chromeos.cc b/chrome/browser/chromeos/policy/restore_on_startup_browsertest_chromeos.cc new file mode 100644 index 0000000..81bb0564 --- /dev/null +++ b/chrome/browser/chromeos/policy/restore_on_startup_browsertest_chromeos.cc
@@ -0,0 +1,97 @@ +// 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/command_line.h" +#include "base/macros.h" +#include "base/memory/scoped_ptr.h" +#include "base/values.h" +#include "chrome/browser/chrome_notification_types.h" +#include "chrome/browser/chromeos/policy/login_policy_test_base.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 "chrome/common/chrome_switches.h" +#include "content/public/browser/notification_service.h" +#include "content/public/browser/web_contents.h" +#include "content/public/test/test_utils.h" +#include "policy/policy_constants.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "url/gurl.h" + +namespace policy { + +namespace { +const char kStartUpURL1[] = "chrome://chrome/"; +const char kStartUpURL2[] = "chrome://version/"; +} + +// Verifies that the |kRestoreOnStartup| and |kRestoreOnStartupURLs| policies +// are honored on Chrome OS. +class RestoreOnStartupTestChromeOS : public LoginPolicyTestBase { + public: + RestoreOnStartupTestChromeOS(); + + // LoginPolicyTestBase: + void SetUpCommandLine(base::CommandLine* command_line) override; + scoped_ptr<base::DictionaryValue> GetMandatoryPoliciesValue() const override; + + void LogInAndVerifyStartUpURLs(); + + private: + DISALLOW_COPY_AND_ASSIGN(RestoreOnStartupTestChromeOS); +}; + +RestoreOnStartupTestChromeOS::RestoreOnStartupTestChromeOS() { +} + +void RestoreOnStartupTestChromeOS::SetUpCommandLine( + base::CommandLine* command_line) { + LoginPolicyTestBase::SetUpCommandLine(command_line); + command_line->AppendSwitch(switches::kDisableChildAccountDetection); +} + +scoped_ptr<base::DictionaryValue> +RestoreOnStartupTestChromeOS::GetMandatoryPoliciesValue() const { + scoped_ptr<base::DictionaryValue> policy(new base::DictionaryValue); + policy->SetInteger(key::kRestoreOnStartup, + SessionStartupPref::kPrefValueURLs); + scoped_ptr<base::ListValue> urls(new base::ListValue); + urls->AppendString(kStartUpURL1); + urls->AppendString(kStartUpURL2); + policy->Set(key::kRestoreOnStartupURLs, urls.Pass()); + return policy; +} + +void RestoreOnStartupTestChromeOS::LogInAndVerifyStartUpURLs() { + LogIn(kAccountId, kAccountPassword); + + const BrowserList* const browser_list = + BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_ASH); + ASSERT_EQ(1U, browser_list->size()); + const Browser* const browser = browser_list->get(0); + ASSERT_TRUE(browser); + const TabStripModel* tabs = browser->tab_strip_model(); + ASSERT_TRUE(tabs); + ASSERT_EQ(2, tabs->count()); + EXPECT_EQ(GURL(kStartUpURL1), tabs->GetWebContentsAt(0)->GetURL()); + EXPECT_EQ(GURL(kStartUpURL2), tabs->GetWebContentsAt(1)->GetURL()); +} + +// Verify that the policies are honored on a new user's login. +IN_PROC_BROWSER_TEST_F(RestoreOnStartupTestChromeOS, PRE_LogInAndVerify) { + SkipToLoginScreen(); + LogInAndVerifyStartUpURLs(); +} + +// Verify that the policies are honored on an existing user's login. +IN_PROC_BROWSER_TEST_F(RestoreOnStartupTestChromeOS, LogInAndVerify) { + content::WindowedNotificationObserver( + chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE, + content::NotificationService::AllSources()).Wait(); + LogInAndVerifyStartUpURLs(); +} + +} // namespace policy
diff --git a/chrome/browser/chromeos/policy/status_uploader_unittest.cc b/chrome/browser/chromeos/policy/status_uploader_unittest.cc index 4ef34eb..13a033c 100644 --- a/chrome/browser/chromeos/policy/status_uploader_unittest.cc +++ b/chrome/browser/chromeos/policy/status_uploader_unittest.cc
@@ -7,8 +7,7 @@ #include "base/time/time.h" #include "chrome/browser/chromeos/policy/device_status_collector.h" #include "chrome/browser/chromeos/policy/status_uploader.h" -#include "chrome/browser/chromeos/settings/device_settings_service.h" -#include "chrome/browser/chromeos/settings/stub_cros_settings_provider.h" +#include "chrome/browser/chromeos/settings/scoped_cros_settings_test_helper.h" #include "chromeos/settings/cros_settings_names.h" #include "components/policy/core/common/cloud/cloud_policy_client.h" #include "components/policy/core/common/cloud/mock_cloud_policy_client.h" @@ -46,35 +45,18 @@ namespace policy { class StatusUploaderTest : public testing::Test { public: - StatusUploaderTest() - : task_runner_(new base::TestSimpleTaskRunner()), - device_settings_provider_(nullptr) { + StatusUploaderTest() : task_runner_(new base::TestSimpleTaskRunner()) { DeviceStatusCollector::RegisterPrefs(prefs_.registry()); } void SetUp() override { client_.SetDMToken("dm_token"); collector_.reset(new MockDeviceStatusCollector(&prefs_)); - - // Swap out the DeviceSettingsProvider with our stub settings provider - // so we can set values for the upload frequency. - chromeos::CrosSettings* cros_settings = chromeos::CrosSettings::Get(); - device_settings_provider_ = - cros_settings->GetProvider(chromeos::kReportDeviceVersionInfo); - EXPECT_TRUE(device_settings_provider_); - EXPECT_TRUE( - cros_settings->RemoveSettingsProvider(device_settings_provider_)); - cros_settings->AddSettingsProvider(&stub_settings_provider_); - + settings_helper_.ReplaceProvider(chromeos::kReportUploadFrequency); } void TearDown() override { content::RunAllBlockingPoolTasksUntilIdle(); - // Restore the real DeviceSettingsProvider. - chromeos::CrosSettings* cros_settings = chromeos::CrosSettings::Get(); - EXPECT_TRUE(cros_settings->RemoveSettingsProvider( - &stub_settings_provider_)); - cros_settings->AddSettingsProvider(device_settings_provider_); } // Given a pending task to upload status, mocks out a server response. @@ -114,11 +96,8 @@ base::MessageLoop loop_; scoped_refptr<base::TestSimpleTaskRunner> task_runner_; - chromeos::ScopedTestDeviceSettingsService test_device_settings_service_; - chromeos::ScopedTestCrosSettings test_cros_settings_; + chromeos::ScopedCrosSettingsTestHelper settings_helper_; scoped_ptr<MockDeviceStatusCollector> collector_; - chromeos::CrosSettingsProvider* device_settings_provider_; - chromeos::StubCrosSettingsProvider stub_settings_provider_; MockCloudPolicyClient client_; MockDeviceManagementService device_management_service_; TestingPrefServiceSimple prefs_; @@ -137,8 +116,7 @@ // when it is passed to the StatusUploader constructor below. MockDeviceStatusCollector* const mock_collector = collector_.get(); const int new_delay = StatusUploader::kDefaultUploadDelayMs * 2; - chromeos::CrosSettings::Get()->SetInteger(chromeos::kReportUploadFrequency, - new_delay); + settings_helper_.SetInteger(chromeos::kReportUploadFrequency, new_delay); const base::TimeDelta expected_delay = base::TimeDelta::FromMilliseconds( new_delay); EXPECT_TRUE(task_runner_->GetPendingTasks().empty()); @@ -202,8 +180,7 @@ // Change the frequency. The new frequency should be reflected in the timing // used for the next callback. const int new_delay = StatusUploader::kDefaultUploadDelayMs * 2; - chromeos::CrosSettings::Get()->SetInteger(chromeos::kReportUploadFrequency, - new_delay); + settings_helper_.SetInteger(chromeos::kReportUploadFrequency, new_delay); const base::TimeDelta expected_delay = base::TimeDelta::FromMilliseconds( new_delay); RunPendingUploadTaskAndCheckNext(uploader, expected_delay);
diff --git a/chrome/browser/chromeos/power/cpu_data_collector.cc b/chrome/browser/chromeos/power/cpu_data_collector.cc index 5b30252..bc43420 100644 --- a/chrome/browser/chromeos/power/cpu_data_collector.cc +++ b/chrome/browser/chromeos/power/cpu_data_collector.cc
@@ -382,7 +382,7 @@ const std::vector<CpuDataCollector::StateOccupancySample>* idle_samples, const std::vector<std::string>* cpu_freq_state_names, const std::vector<CpuDataCollector::StateOccupancySample>* freq_samples) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); cpu_count_ = *cpu_count;
diff --git a/chrome/browser/chromeos/power/peripheral_battery_observer.cc b/chrome/browser/chromeos/power/peripheral_battery_observer.cc index 91cedf6c..a36a5edc73 100644 --- a/chrome/browser/chromeos/power/peripheral_battery_observer.cc +++ b/chrome/browser/chromeos/power/peripheral_battery_observer.cc
@@ -107,7 +107,7 @@ const std::string& path, const std::string& name, int level) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); std::string address; if (IsBluetoothHIDBattery(path)) { // For HID bluetooth device, device address is used as key to index @@ -178,7 +178,7 @@ } void PeripheralBatteryObserver::RemoveBattery(const std::string& address) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); std::string address_lowercase = address; base::StringToLowerASCII(&address_lowercase); std::map<std::string, BatteryInfo>::iterator it =
diff --git a/chrome/browser/chromeos/set_time_dialog.cc b/chrome/browser/chromeos/set_time_dialog.cc index d561f34..62ceebb 100644 --- a/chrome/browser/chromeos/set_time_dialog.cc +++ b/chrome/browser/chromeos/set_time_dialog.cc
@@ -66,8 +66,7 @@ void SetTimeDialog::OnCloseContents(WebContents* source, bool* out_close_dialog) { - if (out_close_dialog) - *out_close_dialog = true; + *out_close_dialog = true; } bool SetTimeDialog::ShouldShowDialogTitle() const {
diff --git a/chrome/browser/chromeos/settings/cros_settings_unittest.cc b/chrome/browser/chromeos/settings/cros_settings_unittest.cc index b0714f0..45ebe62 100644 --- a/chrome/browser/chromeos/settings/cros_settings_unittest.cc +++ b/chrome/browser/chromeos/settings/cros_settings_unittest.cc
@@ -43,7 +43,7 @@ } void FetchPref(const std::string& pref) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); if (expected_props_.find(pref) == expected_props_.end()) return; @@ -65,7 +65,7 @@ } void SetPref(const std::string& pref_name, const base::Value* value) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); settings_.Set(pref_name, *value); }
diff --git a/chrome/browser/chromeos/settings/device_oauth2_token_service_factory.cc b/chrome/browser/chromeos/settings/device_oauth2_token_service_factory.cc index 8cc8746..65e319c 100644 --- a/chrome/browser/chromeos/settings/device_oauth2_token_service_factory.cc +++ b/chrome/browser/chromeos/settings/device_oauth2_token_service_factory.cc
@@ -20,13 +20,13 @@ // static DeviceOAuth2TokenService* DeviceOAuth2TokenServiceFactory::Get() { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); return g_device_oauth2_token_service_; } // static void DeviceOAuth2TokenServiceFactory::Initialize() { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); DCHECK(!g_device_oauth2_token_service_); g_device_oauth2_token_service_ = new DeviceOAuth2TokenService( g_browser_process->system_request_context(), @@ -35,7 +35,7 @@ // static void DeviceOAuth2TokenServiceFactory::Shutdown() { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); if (g_device_oauth2_token_service_) { delete g_device_oauth2_token_service_; g_device_oauth2_token_service_ = NULL;
diff --git a/chrome/browser/chromeos/settings/device_settings_provider.cc b/chrome/browser/chromeos/settings/device_settings_provider.cc index a33b23f9..05fe7685 100644 --- a/chrome/browser/chromeos/settings/device_settings_provider.cc +++ b/chrome/browser/chromeos/settings/device_settings_provider.cc
@@ -56,10 +56,14 @@ kAllowedConnectionTypesForUpdate, kAttestationForContentProtectionEnabled, kDeviceAttestationEnabled, + kDeviceDisabled, + kDeviceDisabledMessage, kDeviceOwner, + kExtensionCacheSize, kHeartbeatEnabled, kHeartbeatFrequency, kPolicyMissingMitigationMode, + kRebootOnShutdown, kReleaseChannel, kReleaseChannelDelegated, kReportDeviceActivityTimes, @@ -79,10 +83,6 @@ kSystemUse24HourClock, kUpdateDisabled, kVariationsRestrictParameter, - kDeviceDisabled, - kDeviceDisabledMessage, - kRebootOnShutdown, - kExtensionCacheSize, }; bool HasOldMetricsFile() {
diff --git a/chrome/browser/chromeos/settings/device_settings_test_helper.h b/chrome/browser/chromeos/settings/device_settings_test_helper.h index 0e5742e..0736342 100644 --- a/chrome/browser/chromeos/settings/device_settings_test_helper.h +++ b/chrome/browser/chromeos/settings/device_settings_test_helper.h
@@ -19,7 +19,6 @@ #include "chrome/browser/chromeos/login/users/scoped_user_manager_enabler.h" #include "chrome/browser/chromeos/policy/device_policy_builder.h" #include "chrome/browser/chromeos/settings/device_settings_service.h" -#include "chrome/browser/chromeos/settings/device_settings_test_helper.h" #include "chromeos/dbus/session_manager_client.h" #include "components/ownership/mock_owner_key_util.h" #include "content/public/test/test_browser_thread_bundle.h"
diff --git a/chrome/browser/chromeos/settings/scoped_cros_settings_test_helper.cc b/chrome/browser/chromeos/settings/scoped_cros_settings_test_helper.cc new file mode 100644 index 0000000..bcbb947e9 --- /dev/null +++ b/chrome/browser/chromeos/settings/scoped_cros_settings_test_helper.cc
@@ -0,0 +1,132 @@ +// 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 "chrome/browser/chromeos/settings/scoped_cros_settings_test_helper.h" + +#include "base/logging.h" +#include "base/values.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/chromeos/ownership/fake_owner_settings_service.h" +#include "chrome/browser/chromeos/ownership/owner_settings_service_chromeos.h" +#include "chrome/browser/chromeos/policy/proto/chrome_device_policy.pb.h" +#include "chrome/browser/chromeos/settings/cros_settings.h" +#include "chrome/browser/chromeos/settings/device_settings_cache.h" +#include "chrome/browser/chromeos/settings/device_settings_service.h" +#include "chrome/browser/profiles/profile.h" +#include "components/ownership/mock_owner_key_util.h" +#include "policy/proto/device_management_backend.pb.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace chromeos { + +ScopedCrosSettingsTestHelper::ScopedCrosSettingsTestHelper() { + Initialize(true); +} + +ScopedCrosSettingsTestHelper::ScopedCrosSettingsTestHelper( + bool create_settings_service) { + Initialize(create_settings_service); +} + +ScopedCrosSettingsTestHelper::~ScopedCrosSettingsTestHelper() { + RestoreProvider(); +} + +scoped_ptr<FakeOwnerSettingsService> +ScopedCrosSettingsTestHelper::CreateOwnerSettingsService(Profile* profile) { + return make_scoped_ptr(new FakeOwnerSettingsService( + profile, new ownership::MockOwnerKeyUtil(), &stub_settings_provider_)); +} + +void ScopedCrosSettingsTestHelper::ReplaceProvider(const std::string& path) { + CHECK(!real_settings_provider_); + // Swap out the DeviceSettingsProvider with our settings provider so we can + // set values for the specified path. + CrosSettings* const cros_settings = CrosSettings::Get(); + real_settings_provider_ = cros_settings->GetProvider(path); + EXPECT_TRUE(real_settings_provider_); + EXPECT_TRUE(cros_settings->RemoveSettingsProvider(real_settings_provider_)); + cros_settings->AddSettingsProvider(&stub_settings_provider_); +} + +void ScopedCrosSettingsTestHelper::RestoreProvider() { + if (real_settings_provider_) { + // Restore the real DeviceSettingsProvider. + CrosSettings* const cros_settings = CrosSettings::Get(); + EXPECT_TRUE( + cros_settings->RemoveSettingsProvider(&stub_settings_provider_)); + cros_settings->AddSettingsProvider(real_settings_provider_); + real_settings_provider_ = nullptr; + } +} + +void ScopedCrosSettingsTestHelper::SetTrustedStatus( + CrosSettingsProvider::TrustedStatus status) { + stub_settings_provider_.SetTrustedStatus(status); +} + +void ScopedCrosSettingsTestHelper::SetCurrentUserIsOwner(bool owner) { + stub_settings_provider_.SetCurrentUserIsOwner(owner); +} + +void ScopedCrosSettingsTestHelper::Set(const std::string& path, + const base::Value& in_value) { + stub_settings_provider_.Set(path, in_value); +} + +void ScopedCrosSettingsTestHelper::SetBoolean(const std::string& path, + bool in_value) { + Set(path, base::FundamentalValue(in_value)); +} + +void ScopedCrosSettingsTestHelper::SetInteger(const std::string& path, + int in_value) { + Set(path, base::FundamentalValue(in_value)); +} + +void ScopedCrosSettingsTestHelper::SetDouble(const std::string& path, + double in_value) { + Set(path, base::FundamentalValue(in_value)); +} + +void ScopedCrosSettingsTestHelper::SetString(const std::string& path, + const std::string& in_value) { + Set(path, base::StringValue(in_value)); +} + +void ScopedCrosSettingsTestHelper::StoreCachedDeviceSetting( + const std::string& path) { + const base::Value* const value = stub_settings_provider_.Get(path); + if (value) { + enterprise_management::PolicyData data; + enterprise_management::ChromeDeviceSettingsProto settings; + if (device_settings_cache::Retrieve(&data, + g_browser_process->local_state())) { + CHECK(settings.ParseFromString(data.policy_value())); + } + OwnerSettingsServiceChromeOS::UpdateDeviceSettings(path, *value, settings); + CHECK(settings.SerializeToString(data.mutable_policy_value())); + CHECK(device_settings_cache::Store(data, g_browser_process->local_state())); + } +} + +void ScopedCrosSettingsTestHelper::CopyStoredValue(const std::string& path) { + CrosSettingsProvider* provider = real_settings_provider_ + ? real_settings_provider_ + : CrosSettings::Get()->GetProvider(path); + const base::Value* const value = provider->Get(path); + if (value) { + stub_settings_provider_.Set(path, *value); + } +} + +void ScopedCrosSettingsTestHelper::Initialize(bool create_settings_service) { + if (create_settings_service) { + CHECK(!DeviceSettingsService::IsInitialized()); + test_device_settings_service_.reset(new ScopedTestDeviceSettingsService()); + test_cros_settings_.reset(new ScopedTestCrosSettings()); + } +} + +} // namespace chromeos
diff --git a/chrome/browser/chromeos/settings/scoped_cros_settings_test_helper.h b/chrome/browser/chromeos/settings/scoped_cros_settings_test_helper.h new file mode 100644 index 0000000..86a07bd --- /dev/null +++ b/chrome/browser/chromeos/settings/scoped_cros_settings_test_helper.h
@@ -0,0 +1,83 @@ +// 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 CHROME_BROWSER_CHROMEOS_SETTINGS_SCOPED_CROS_SETTINGS_TEST_HELPER_H_ +#define CHROME_BROWSER_CHROMEOS_SETTINGS_SCOPED_CROS_SETTINGS_TEST_HELPER_H_ + +#include <string> + +#include "base/macros.h" +#include "base/memory/scoped_ptr.h" +#include "chrome/browser/chromeos/settings/stub_cros_settings_provider.h" +#include "chromeos/settings/cros_settings_provider.h" + +class Profile; + +namespace base { +class Value; +} + +namespace chromeos { + +class FakeOwnerSettingsService; +class ScopedTestCrosSettings; +class ScopedTestDeviceSettingsService; + +class ScopedCrosSettingsTestHelper { + public: + ScopedCrosSettingsTestHelper(); + + // In some cases it is required to pass |create_settings_service| as false: + // If the test already has a device settings service and/or CrosSettings set + // up by another (instantiated or base) class, creating another one causes + // crash. + explicit ScopedCrosSettingsTestHelper(bool create_settings_service); + ~ScopedCrosSettingsTestHelper(); + + // Methods to replace and restore CrosSettingsProvider for the specified + // |path|. + void ReplaceProvider(const std::string& path); + void RestoreProvider(); + + // Method to create an owner settings service that uses + // |stub_settings_provider_| as settings write path. + scoped_ptr<FakeOwnerSettingsService> CreateOwnerSettingsService( + Profile* profile); + + // These methods simply call the according |stub_settings_provider_| method. + void SetTrustedStatus(CrosSettingsProvider::TrustedStatus status); + void SetCurrentUserIsOwner(bool owner); + void Set(const std::string& path, const base::Value& in_value); + + // Convenience forms of Set() from CrosSettingsProvider. These methods will + // replace any existing value at that |path|, even if it has a different type. + void SetBoolean(const std::string& path, bool in_value); + void SetInteger(const std::string& path, int in_value); + void SetDouble(const std::string& path, double in_value); + void SetString(const std::string& path, const std::string& in_value); + + // This may be called before |ReplaceProvider| to copy values currently stored + // in the old provider. If the method is called after |ReplaceProvider|, then + // the value is retreived from |real_settings_provider_| for any |path|. + void CopyStoredValue(const std::string& path); + + // Write the setting from |path| to local state so that it can be retreived + // later on browser test startup by the device settings service. + void StoreCachedDeviceSetting(const std::string& path); + + private: + // Helpers used to mock out cros settings. + scoped_ptr<ScopedTestDeviceSettingsService> test_device_settings_service_; + scoped_ptr<ScopedTestCrosSettings> test_cros_settings_; + CrosSettingsProvider* real_settings_provider_ = nullptr; + StubCrosSettingsProvider stub_settings_provider_; + + void Initialize(bool create_settings_service); + + DISALLOW_COPY_AND_ASSIGN(ScopedCrosSettingsTestHelper); +}; + +} // namespace chromeos + +#endif // CHROME_BROWSER_CHROMEOS_SETTINGS_SCOPED_CROS_SETTINGS_TEST_HELPER_H_
diff --git a/chrome/browser/chromeos/settings/shutdown_policy_handler_unittest.cc b/chrome/browser/chromeos/settings/shutdown_policy_handler_unittest.cc index eadd410..8b9669f9 100644 --- a/chrome/browser/chromeos/settings/shutdown_policy_handler_unittest.cc +++ b/chrome/browser/chromeos/settings/shutdown_policy_handler_unittest.cc
@@ -6,15 +6,10 @@ #include "base/bind.h" #include "base/bind_helpers.h" -#include "base/command_line.h" -#include "base/memory/scoped_ptr.h" #include "base/run_loop.h" -#include "base/values.h" -#include "chrome/browser/chromeos/settings/device_settings_service.h" -#include "chromeos/chromeos_switches.h" +#include "chrome/browser/chromeos/settings/scoped_cros_settings_test_helper.h" #include "chromeos/dbus/dbus_thread_manager.h" #include "chromeos/settings/cros_settings_names.h" -#include "chromeos/settings/cros_settings_provider.h" #include "content/public/test/test_browser_thread_bundle.h" #include "testing/gtest/include/gtest/gtest.h" @@ -30,29 +25,21 @@ protected: ShutdownPolicyHandlerTest() - : cros_settings_(nullptr), - callback_called_(false), + : callback_called_(false), reboot_on_shutdown_(false), - delegate_invocations_count_(0) { - base::CommandLine::ForCurrentProcess()->AppendSwitch( - switches::kStubCrosSettings); - test_cros_settings_.reset(new ScopedTestCrosSettings); -} + delegate_invocations_count_(0) {} // testing::Test: -void SetUp() override { + void SetUp() override { testing::Test::SetUp(); - cros_settings_ = CrosSettings::Get(); DBusThreadManager::Initialize(); + settings_helper_.ReplaceProvider(kRebootOnShutdown); } void TearDown() override { DBusThreadManager::Shutdown(); } void SetRebootOnShutdown(bool reboot_on_shutdown) { - const base::FundamentalValue reboot_on_shutdown_value(reboot_on_shutdown); - CrosSettings::Get() - ->GetProvider(kRebootOnShutdown) - ->Set(kRebootOnShutdown, reboot_on_shutdown_value); + settings_helper_.SetBoolean(kRebootOnShutdown, reboot_on_shutdown); base::RunLoop().RunUntilIdle(); } @@ -64,20 +51,14 @@ protected: content::TestBrowserThreadBundle thread_bundle_; - - CrosSettings* cros_settings_; - scoped_ptr<CrosSettingsProvider> device_settings_provider_; - - ScopedTestDeviceSettingsService test_device_settings_service_; - scoped_ptr<ScopedTestCrosSettings> test_cros_settings_; - + ScopedCrosSettingsTestHelper settings_helper_; bool callback_called_; bool reboot_on_shutdown_; int delegate_invocations_count_; }; TEST_F(ShutdownPolicyHandlerTest, RetrieveTrustedDevicePolicies) { - ShutdownPolicyHandler shutdown_policy_observer(cros_settings_, this); + ShutdownPolicyHandler shutdown_policy_observer(CrosSettings::Get(), this); base::RunLoop().RunUntilIdle(); EXPECT_EQ(0, delegate_invocations_count_); @@ -100,7 +81,7 @@ } TEST_F(ShutdownPolicyHandlerTest, CheckIfRebootOnShutdown) { - ShutdownPolicyHandler shutdown_policy_observer(cros_settings_, this); + ShutdownPolicyHandler shutdown_policy_observer(CrosSettings::Get(), this); base::RunLoop().RunUntilIdle(); // Allow shutdown.
diff --git a/chrome/browser/chromeos/settings/stub_cros_settings_provider.cc b/chrome/browser/chromeos/settings/stub_cros_settings_provider.cc index c11e3c30..3b6a89a2 100644 --- a/chrome/browser/chromeos/settings/stub_cros_settings_provider.cc +++ b/chrome/browser/chromeos/settings/stub_cros_settings_provider.cc
@@ -37,17 +37,27 @@ CrosSettingsProvider::TrustedStatus StubCrosSettingsProvider::PrepareTrustedValues(const base::Closure& cb) { - // We don't have a trusted store so all values are available immediately. - return TRUSTED; + return trusted_status_; } bool StubCrosSettingsProvider::HandlesSetting(const std::string& path) const { return DeviceSettingsProvider::IsDeviceSetting(path); } +void StubCrosSettingsProvider::SetTrustedStatus(TrustedStatus status) { + trusted_status_ = status; +} + +void StubCrosSettingsProvider::SetCurrentUserIsOwner(bool owner) { + current_user_is_owner_ = owner; +} + void StubCrosSettingsProvider::DoSet(const std::string& path, const base::Value& value) { - values_.SetValue(path, value.DeepCopy()); + if (current_user_is_owner_) + values_.SetValue(path, value.DeepCopy()); + else + LOG(WARNING) << "Changing settings from non-owner, setting=" << path; NotifyObservers(path); }
diff --git a/chrome/browser/chromeos/settings/stub_cros_settings_provider.h b/chrome/browser/chromeos/settings/stub_cros_settings_provider.h index 5fcb65c1..4d54a3e 100644 --- a/chrome/browser/chromeos/settings/stub_cros_settings_provider.h +++ b/chrome/browser/chromeos/settings/stub_cros_settings_provider.h
@@ -24,6 +24,9 @@ TrustedStatus PrepareTrustedValues(const base::Closure& callback) override; bool HandlesSetting(const std::string& path) const override; + void SetTrustedStatus(TrustedStatus status); + void SetCurrentUserIsOwner(bool owner); + private: // CrosSettingsProvider implementation: void DoSet(const std::string& path, const base::Value& value) override; @@ -34,6 +37,14 @@ // In-memory settings storage. PrefValueMap values_; + // Some tests imply that calling Set() as non-owner doesn't change the actual + // value but still trigger a notification. For such cases, it is possible to + // emulate this behavior by changing the ownership status to non-owner with + // |SetCurrentUserIsOwner(false)|. + bool current_user_is_owner_ = true; + + TrustedStatus trusted_status_ = CrosSettingsProvider::TRUSTED; + DISALLOW_COPY_AND_ASSIGN(StubCrosSettingsProvider); };
diff --git a/chrome/browser/chromeos/sim_dialog_delegate.cc b/chrome/browser/chromeos/sim_dialog_delegate.cc index 4321b1c8..babd901 100644 --- a/chrome/browser/chromeos/sim_dialog_delegate.cc +++ b/chrome/browser/chromeos/sim_dialog_delegate.cc
@@ -101,8 +101,7 @@ void SimDialogDelegate::OnCloseContents(WebContents* source, bool* out_close_dialog) { - if (out_close_dialog) - *out_close_dialog = true; + *out_close_dialog = true; } bool SimDialogDelegate::ShouldShowDialogTitle() const {
diff --git a/chrome/browser/chromeos/system/device_disabling_browsertest.cc b/chrome/browser/chromeos/system/device_disabling_browsertest.cc index 1470800..62e4cb8d 100644 --- a/chrome/browser/chromeos/system/device_disabling_browsertest.cc +++ b/chrome/browser/chromeos/system/device_disabling_browsertest.cc
@@ -48,9 +48,12 @@ } // namespace +// Boolean parameter is used to run this test for webview (true) and for +// iframe (false) GAIA sign in. class DeviceDisablingTest : public OobeBaseTest, - public NetworkStateInformer::NetworkStateInformerObserver { + public NetworkStateInformer::NetworkStateInformerObserver, + public testing::WithParamInterface<bool> { public: DeviceDisablingTest(); @@ -80,6 +83,7 @@ DeviceDisablingTest::DeviceDisablingTest() : fake_session_manager_client_(new FakeSessionManagerClient) { + set_use_webview(GetParam()); } void DeviceDisablingTest::MarkDisabledAndWaitForPolicyFetch() { @@ -135,8 +139,7 @@ network_state_change_wait_run_loop_.Quit(); } - -IN_PROC_BROWSER_TEST_F(DeviceDisablingTest, DisableDuringNormalOperation) { +IN_PROC_BROWSER_TEST_P(DeviceDisablingTest, DisableDuringNormalOperation) { // Mark the device as disabled and wait until cros settings update. MarkDisabledAndWaitForPolicyFetch(); @@ -155,7 +158,7 @@ // causes the UI to try and show the login screen after some delay. It must // be ensured that the login screen does not show and does not clobber the // disabled screen. -IN_PROC_BROWSER_TEST_F(DeviceDisablingTest, DisableWithEphemeralUsers) { +IN_PROC_BROWSER_TEST_P(DeviceDisablingTest, DisableWithEphemeralUsers) { // Connect to the fake Ethernet network. This ensures that Chrome OS will not // try to show the offline error screen. base::RunLoop connect_run_loop; @@ -219,5 +222,9 @@ EXPECT_EQ(OobeUI::kScreenDeviceDisabled, GetCurrentScreenName(web_contents)); } +INSTANTIATE_TEST_CASE_P(DeviceDisablingSuite, + DeviceDisablingTest, + testing::Bool()); + } // namespace system } // namespace chromeos
diff --git a/chrome/browser/chromeos/system/input_device_settings_impl_ozone.cc b/chrome/browser/chromeos/system/input_device_settings_impl_ozone.cc index 60791f94..b9e92df6 100644 --- a/chrome/browser/chromeos/system/input_device_settings_impl_ozone.cc +++ b/chrome/browser/chromeos/system/input_device_settings_impl_ozone.cc
@@ -60,7 +60,7 @@ void InputDeviceSettingsImplOzone::TouchpadExists( const DeviceExistsCallback& callback) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); callback.Run(input_controller_->HasTouchpad()); } @@ -99,7 +99,7 @@ void InputDeviceSettingsImplOzone::MouseExists( const DeviceExistsCallback& callback) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); callback.Run(input_controller_->HasMouse()); }
diff --git a/chrome/browser/chromeos/system/input_device_settings_impl_x11.cc b/chrome/browser/chromeos/system/input_device_settings_impl_x11.cc index aa41497a..17c06499 100644 --- a/chrome/browser/chromeos/system/input_device_settings_impl_x11.cc +++ b/chrome/browser/chromeos/system/input_device_settings_impl_x11.cc
@@ -62,7 +62,7 @@ } void ExecuteScript(const std::vector<std::string>& argv) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); if (argv.size() == 1) return; @@ -115,14 +115,14 @@ void RunCallbackUIThread( scoped_refptr<RefCountedBool> exists, const InputDeviceSettings::DeviceExistsCallback& callback) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); DVLOG(1) << "RunCallbackUIThread " << exists->data; callback.Run(exists->data); } void DeviceExists(const char* script, const InputDeviceSettings::DeviceExistsCallback& callback) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); // One or both of the control scripts can apparently hang during shutdown // (http://crbug.com/255546). Run the blocking pool task with
diff --git a/chrome/browser/chromeos/system_logs/dbus_log_source.cc b/chrome/browser/chromeos/system_logs/dbus_log_source.cc index f7b4e68..7f3b4fc 100644 --- a/chrome/browser/chromeos/system_logs/dbus_log_source.cc +++ b/chrome/browser/chromeos/system_logs/dbus_log_source.cc
@@ -19,7 +19,7 @@ } void DBusLogSource::Fetch(const SysLogsSourceCallback& callback) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); DCHECK(!callback.is_null()); SystemLogsResponse response;
diff --git a/chrome/browser/chromeos/system_logs/debug_daemon_log_source.cc b/chrome/browser/chromeos/system_logs/debug_daemon_log_source.cc index d1780f92..263d03cb 100644 --- a/chrome/browser/chromeos/system_logs/debug_daemon_log_source.cc +++ b/chrome/browser/chromeos/system_logs/debug_daemon_log_source.cc
@@ -39,7 +39,7 @@ DebugDaemonLogSource::~DebugDaemonLogSource() {} void DebugDaemonLogSource::Fetch(const SysLogsSourceCallback& callback) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); DCHECK(!callback.is_null()); DCHECK(callback_.is_null()); @@ -77,7 +77,7 @@ void DebugDaemonLogSource::OnGetRoutes(bool succeeded, const std::vector<std::string>& routes) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); if (succeeded) (*response_)[kRoutesKeyName] = JoinString(routes, '\n'); @@ -88,7 +88,7 @@ void DebugDaemonLogSource::OnGetNetworkStatus(bool succeeded, const std::string& status) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); if (succeeded) (*response_)[kNetworkStatusKeyName] = status;
diff --git a/chrome/browser/chromeos/system_logs/device_event_log_source.cc b/chrome/browser/chromeos/system_logs/device_event_log_source.cc index 0fb8a01..30e57a2c 100644 --- a/chrome/browser/chromeos/system_logs/device_event_log_source.cc +++ b/chrome/browser/chromeos/system_logs/device_event_log_source.cc
@@ -20,7 +20,7 @@ } void DeviceEventLogSource::Fetch(const SysLogsSourceCallback& callback) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); DCHECK(!callback.is_null()); scoped_ptr<SystemLogsResponse> response(new SystemLogsResponse);
diff --git a/chrome/browser/chromeos/ui/choose_mobile_network_dialog.cc b/chrome/browser/chromeos/ui/choose_mobile_network_dialog.cc index 43e4b5c..0da2c869 100644 --- a/chrome/browser/chromeos/ui/choose_mobile_network_dialog.cc +++ b/chrome/browser/chromeos/ui/choose_mobile_network_dialog.cc
@@ -62,8 +62,7 @@ void ChooseMobileNetworkDialog::OnCloseContents(WebContents* source, bool* out_close_dialog) { - if (out_close_dialog) - *out_close_dialog = true; + *out_close_dialog = true; } bool ChooseMobileNetworkDialog::ShouldShowDialogTitle() const {
diff --git a/chrome/browser/chromeos/ui/inline_login_dialog.cc b/chrome/browser/chromeos/ui/inline_login_dialog.cc index b60b32e..35548a1 100644 --- a/chrome/browser/chromeos/ui/inline_login_dialog.cc +++ b/chrome/browser/chromeos/ui/inline_login_dialog.cc
@@ -71,8 +71,7 @@ void InlineLoginDialog::OnCloseContents(content::WebContents* source, bool* out_close_dialog) { - if (out_close_dialog) - *out_close_dialog = true; + *out_close_dialog = true; } bool InlineLoginDialog::ShouldShowDialogTitle() const {
diff --git a/chrome/browser/content_settings/host_content_settings_map_unittest.cc b/chrome/browser/content_settings/host_content_settings_map_unittest.cc index dc14204..6247af2 100644 --- a/chrome/browser/content_settings/host_content_settings_map_unittest.cc +++ b/chrome/browser/content_settings/host_content_settings_map_unittest.cc
@@ -965,6 +965,7 @@ prefs, CONTENT_SETTING_ASK, CONTENT_SETTINGS_TYPE_FULLSCREEN)); + // The mediastream setting is deprecated. EXPECT_FALSE(HostContentSettingsMap::IsSettingAllowedForType( prefs, CONTENT_SETTING_ALLOW, CONTENT_SETTINGS_TYPE_MEDIASTREAM)); @@ -975,6 +976,21 @@ prefs, CONTENT_SETTING_BLOCK, CONTENT_SETTINGS_TYPE_MEDIASTREAM)); + // We support the ALLOW value for media permission exceptions, + // but not as the default setting. + EXPECT_TRUE(HostContentSettingsMap::IsSettingAllowedForType( + prefs, CONTENT_SETTING_ALLOW, + CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC)); + EXPECT_TRUE(HostContentSettingsMap::IsSettingAllowedForType( + prefs, CONTENT_SETTING_ALLOW, + CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA)); + EXPECT_FALSE(HostContentSettingsMap::IsDefaultSettingAllowedForType( + prefs, CONTENT_SETTING_ALLOW, + CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC)); + EXPECT_FALSE(HostContentSettingsMap::IsDefaultSettingAllowedForType( + prefs, CONTENT_SETTING_ALLOW, + CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA)); + // TODO(msramek): Add more checks for setting type - setting pairs where // it is not obvious whether or not they are allowed. }
diff --git a/chrome/browser/custom_handlers/protocol_handler_registry.cc b/chrome/browser/custom_handlers/protocol_handler_registry.cc index 99bfd7cf2..88a20e35 100644 --- a/chrome/browser/custom_handlers/protocol_handler_registry.cc +++ b/chrome/browser/custom_handlers/protocol_handler_registry.cc
@@ -115,19 +115,19 @@ bool ProtocolHandlerRegistry::IOThreadDelegate::IsHandledProtocol( const std::string& scheme) const { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + DCHECK_CURRENTLY_ON(BrowserThread::IO); return enabled_ && !LookupHandler(default_handlers_, scheme).IsEmpty(); } void ProtocolHandlerRegistry::IOThreadDelegate::ClearDefault( const std::string& scheme) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + DCHECK_CURRENTLY_ON(BrowserThread::IO); default_handlers_.erase(scheme); } void ProtocolHandlerRegistry::IOThreadDelegate::SetDefault( const ProtocolHandler& handler) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + DCHECK_CURRENTLY_ON(BrowserThread::IO); ClearDefault(handler.protocol()); default_handlers_.insert(std::make_pair(handler.protocol(), handler)); } @@ -137,7 +137,7 @@ // the url from |request|. net::URLRequestJob* ProtocolHandlerRegistry::IOThreadDelegate::MaybeCreateJob( net::URLRequest* request, net::NetworkDelegate* network_delegate) const { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + DCHECK_CURRENTLY_ON(BrowserThread::IO); ProtocolHandler handler = LookupHandler(default_handlers_, request->url().scheme()); @@ -181,7 +181,7 @@ const std::string& scheme, net::URLRequest* request, net::NetworkDelegate* network_delegate) const { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + DCHECK_CURRENTLY_ON(BrowserThread::IO); net::URLRequestJob* job = io_thread_delegate_->MaybeCreateJob( request, network_delegate); if (job) @@ -208,14 +208,14 @@ bool ProtocolHandlerRegistry::JobInterceptorFactory::IsHandledProtocol( const std::string& scheme) const { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + DCHECK_CURRENTLY_ON(BrowserThread::IO); return io_thread_delegate_->IsHandledProtocol(scheme) || job_factory_->IsHandledProtocol(scheme); } bool ProtocolHandlerRegistry::JobInterceptorFactory::IsHandledURL( const GURL& url) const { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + DCHECK_CURRENTLY_ON(BrowserThread::IO); return (url.is_valid() && io_thread_delegate_->IsHandledProtocol(url.scheme())) || job_factory_->IsHandledURL(url); @@ -223,7 +223,7 @@ bool ProtocolHandlerRegistry::JobInterceptorFactory::IsSafeRedirectTarget( const GURL& location) const { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + DCHECK_CURRENTLY_ON(BrowserThread::IO); return job_factory_->IsSafeRedirectTarget(location); } @@ -350,7 +350,7 @@ void ProtocolHandlerRegistry::OnAcceptRegisterProtocolHandler( const ProtocolHandler& handler) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); RegisterProtocolHandler(handler, USER); SetDefault(handler); Save(); @@ -359,7 +359,7 @@ void ProtocolHandlerRegistry::OnDenyRegisterProtocolHandler( const ProtocolHandler& handler) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); RegisterProtocolHandler(handler, USER); Save(); NotifyChanged(); @@ -367,14 +367,14 @@ void ProtocolHandlerRegistry::OnIgnoreRegisterProtocolHandler( const ProtocolHandler& handler) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); IgnoreProtocolHandler(handler, USER); Save(); NotifyChanged(); } bool ProtocolHandlerRegistry::AttemptReplace(const ProtocolHandler& handler) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); ProtocolHandler old_default = GetHandlerFor(handler.protocol()); bool make_new_handler_default = handler.IsSameOrigin(old_default); ProtocolHandlerList to_replace(GetReplacedHandlers(handler)); @@ -410,7 +410,7 @@ } void ProtocolHandlerRegistry::ClearDefault(const std::string& scheme) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); default_handlers_.erase(scheme); BrowserThread::PostTask( @@ -423,7 +423,7 @@ bool ProtocolHandlerRegistry::IsDefault( const ProtocolHandler& handler) const { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); return GetHandlerFor(handler.protocol()) == handler; } @@ -444,7 +444,7 @@ } void ProtocolHandlerRegistry::InitProtocolSettings() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); // Any further default additions to the table will get rejected from now on. is_loaded_ = true; @@ -484,7 +484,7 @@ } int ProtocolHandlerRegistry::GetHandlerIndex(const std::string& scheme) const { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); const ProtocolHandler& handler = GetHandlerFor(scheme); if (handler.IsEmpty()) return -1; @@ -504,7 +504,7 @@ ProtocolHandlerRegistry::ProtocolHandlerList ProtocolHandlerRegistry::GetHandlersFor( const std::string& scheme) const { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); ProtocolHandlerMultiMap::const_iterator p = protocol_handlers_.find(scheme); if (p == protocol_handlers_.end()) { return ProtocolHandlerList(); @@ -519,7 +519,7 @@ void ProtocolHandlerRegistry::GetRegisteredProtocols( std::vector<std::string>* output) const { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); ProtocolHandlerMultiMap::const_iterator p; for (p = protocol_handlers_.begin(); p != protocol_handlers_.end(); ++p) { if (!p->second.empty()) @@ -529,7 +529,7 @@ bool ProtocolHandlerRegistry::CanSchemeBeOverridden( const std::string& scheme) const { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); const ProtocolHandlerList* handlers = GetHandlerList(scheme); // If we already have a handler for this scheme, we can add more. if (handlers != NULL && !handlers->empty()) @@ -540,7 +540,7 @@ bool ProtocolHandlerRegistry::IsRegistered( const ProtocolHandler& handler) const { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); const ProtocolHandlerList* handlers = GetHandlerList(handler.protocol()); if (!handlers) { return false; @@ -561,7 +561,7 @@ } bool ProtocolHandlerRegistry::IsIgnored(const ProtocolHandler& handler) const { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); ProtocolHandlerList::const_iterator i; for (i = ignored_protocol_handlers_.begin(); i != ignored_protocol_handlers_.end(); ++i) { @@ -574,7 +574,7 @@ bool ProtocolHandlerRegistry::HasRegisteredEquivalent( const ProtocolHandler& handler) const { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); const ProtocolHandlerList* handlers = GetHandlerList(handler.protocol()); if (!handlers) { return false; @@ -590,7 +590,7 @@ bool ProtocolHandlerRegistry::HasIgnoredEquivalent( const ProtocolHandler& handler) const { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); ProtocolHandlerList::const_iterator i; for (i = ignored_protocol_handlers_.begin(); i != ignored_protocol_handlers_.end(); ++i) { @@ -603,7 +603,7 @@ void ProtocolHandlerRegistry::RemoveIgnoredHandler( const ProtocolHandler& handler) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); bool should_notify = false; if (HandlerExists(handler, ignored_protocol_handlers_) && HandlerExists(handler, user_ignored_protocol_handlers_)) { @@ -620,13 +620,13 @@ bool ProtocolHandlerRegistry::IsHandledProtocol( const std::string& scheme) const { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); return enabled_ && !GetHandlerFor(scheme).IsEmpty(); } void ProtocolHandlerRegistry::RemoveHandler( const ProtocolHandler& handler) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); ProtocolHandlerList& handlers = protocol_handlers_[handler.protocol()]; bool erase_success = false; if (HandlerExists(handler, handlers) && @@ -661,7 +661,7 @@ } void ProtocolHandlerRegistry::RemoveDefaultHandler(const std::string& scheme) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); ProtocolHandler current_default = GetHandlerFor(scheme); if (!current_default.IsEmpty()) RemoveHandler(current_default); @@ -669,12 +669,12 @@ const ProtocolHandler& ProtocolHandlerRegistry::GetHandlerFor( const std::string& scheme) const { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); return LookupHandler(default_handlers_, scheme); } void ProtocolHandlerRegistry::Enable() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); if (enabled_) { return; } @@ -693,7 +693,7 @@ } void ProtocolHandlerRegistry::Disable() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); if (!enabled_) { return; } @@ -712,7 +712,7 @@ } void ProtocolHandlerRegistry::Shutdown() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); delegate_.reset(NULL); // We free these now in case there are any outstanding workers running. If // we didn't free them they could respond to workers and try to update the @@ -742,12 +742,12 @@ } ProtocolHandlerRegistry::~ProtocolHandlerRegistry() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); DCHECK(default_client_observers_.empty()); } void ProtocolHandlerRegistry::PromoteHandler(const ProtocolHandler& handler) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); DCHECK(IsRegistered(handler)); ProtocolHandlerMultiMap::iterator p = protocol_handlers_.find(handler.protocol()); @@ -757,7 +757,7 @@ } void ProtocolHandlerRegistry::Save() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); if (is_loading_) { return; } @@ -776,7 +776,7 @@ const ProtocolHandlerRegistry::ProtocolHandlerList* ProtocolHandlerRegistry::GetHandlerList( const std::string& scheme) const { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); ProtocolHandlerMultiMap::const_iterator p = protocol_handlers_.find(scheme); if (p == protocol_handlers_.end()) { return NULL; @@ -785,7 +785,7 @@ } void ProtocolHandlerRegistry::SetDefault(const ProtocolHandler& handler) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); ProtocolHandlerMap::const_iterator p = default_handlers_.find( handler.protocol()); // If we're not loading, and we are setting a default for a new protocol, @@ -802,7 +802,7 @@ } void ProtocolHandlerRegistry::InsertHandler(const ProtocolHandler& handler) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); ProtocolHandlerMultiMap::iterator p = protocol_handlers_.find(handler.protocol()); @@ -817,7 +817,7 @@ } base::Value* ProtocolHandlerRegistry::EncodeRegisteredHandlers() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); base::ListValue* protocol_handlers = new base::ListValue(); for (ProtocolHandlerMultiMap::iterator i = user_protocol_handlers_.begin(); i != user_protocol_handlers_.end(); @@ -835,7 +835,7 @@ } base::Value* ProtocolHandlerRegistry::EncodeIgnoredHandlers() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); base::ListValue* handlers = new base::ListValue(); for (ProtocolHandlerList::iterator i = user_ignored_protocol_handlers_.begin(); @@ -847,7 +847,7 @@ } void ProtocolHandlerRegistry::NotifyChanged() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); content::NotificationService::current()->Notify( chrome::NOTIFICATION_PROTOCOL_HANDLER_REGISTRY_CHANGED, content::Source<content::BrowserContext>(context_), @@ -857,7 +857,7 @@ void ProtocolHandlerRegistry::RegisterProtocolHandler( const ProtocolHandler& handler, const HandlerSource source) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); DCHECK(CanSchemeBeOverridden(handler.protocol())); DCHECK(!handler.IsEmpty()); ProtocolHandlerMultiMap& map = @@ -875,7 +875,7 @@ std::vector<const base::DictionaryValue*> ProtocolHandlerRegistry::GetHandlersFromPref(const char* pref_name) const { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); std::vector<const base::DictionaryValue*> result; PrefService* prefs = user_prefs::UserPrefs::Get(context_); if (!prefs->HasPrefPath(pref_name)) { @@ -917,7 +917,7 @@ void ProtocolHandlerRegistry::IgnoreProtocolHandler( const ProtocolHandler& handler, const HandlerSource source) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); ProtocolHandlerList& list = (source == POLICY) ? policy_ignored_protocol_handlers_ : user_ignored_protocol_handlers_; @@ -970,7 +970,7 @@ scoped_ptr<ProtocolHandlerRegistry::JobInterceptorFactory> ProtocolHandlerRegistry::CreateJobInterceptorFactory() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); // this is always created on the UI thread (in profile_io's // InitializeOnUIThread. Any method calls must be done // on the IO thread (this is checked).
diff --git a/chrome/browser/custom_handlers/protocol_handler_registry_unittest.cc b/chrome/browser/custom_handlers/protocol_handler_registry_unittest.cc index ffdf7dee..61da17f6 100644 --- a/chrome/browser/custom_handlers/protocol_handler_registry_unittest.cc +++ b/chrome/browser/custom_handlers/protocol_handler_registry_unittest.cc
@@ -34,7 +34,7 @@ void AssertInterceptedIO( const GURL& url, net::URLRequestJobFactory* interceptor) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + DCHECK_CURRENTLY_ON(BrowserThread::IO); net::URLRequestContext context; scoped_ptr<net::URLRequest> request(context.CreateRequest( url, net::DEFAULT_PRIORITY, NULL)); @@ -47,7 +47,7 @@ void AssertIntercepted( const GURL& url, net::URLRequestJobFactory* interceptor) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, base::Bind(AssertInterceptedIO, @@ -96,7 +96,7 @@ const std::string& scheme, bool expected, ProtocolHandlerRegistry::JobInterceptorFactory* interceptor) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + DCHECK_CURRENTLY_ON(BrowserThread::IO); interceptor->Chain(scoped_ptr<net::URLRequestJobFactory>( new FakeURLRequestJobFactory())); ASSERT_EQ(expected, interceptor->IsHandledProtocol(scheme)); @@ -107,7 +107,7 @@ const std::string& scheme, bool expected, ProtocolHandlerRegistry::JobInterceptorFactory* interceptor) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, base::Bind(AssertWillHandleIO,
diff --git a/chrome/browser/download/download_commands.cc b/chrome/browser/download/download_commands.cc index 97784977..d571907 100644 --- a/chrome/browser/download/download_commands.cc +++ b/chrome/browser/download/download_commands.cc
@@ -4,6 +4,7 @@ #include "chrome/browser/download/download_commands.h" +#include "base/strings/stringprintf.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/download/download_crx_util.h" #include "chrome/browser/download/download_item_model.h" @@ -15,7 +16,9 @@ #include "chrome/browser/ui/scoped_tabbed_browser_displayer.h" #include "chrome/common/url_constants.h" #include "chrome/grit/generated_resources.h" +#include "components/google/core/browser/google_util.h" #include "grit/theme_resources.h" +#include "net/base/url_util.h" #include "ui/base/l10n/l10n_util.h" #include "ui/base/resource/resource_bundle.h" @@ -29,7 +32,7 @@ DCHECK(download_item); } -int DownloadCommands::GetCommandIconId(Command command) { +int DownloadCommands::GetCommandIconId(Command command) const { switch (command) { case PAUSE: return IDR_DOWNLOAD_NOTIFICATION_MENU_PAUSE; @@ -58,6 +61,16 @@ return -1; } +GURL DownloadCommands::GetLearnMoreURLForInterruptedDownload() const { + GURL learn_more_url(chrome::kDownloadInterruptedLearnMoreURL); + learn_more_url = google_util::AppendGoogleLocaleParam( + learn_more_url, g_browser_process->GetApplicationLocale()); + return net::AppendQueryParameter( + learn_more_url, "ctx", + base::StringPrintf("%d", + static_cast<int>(download_item_->GetLastReason()))); +} + gfx::Image DownloadCommands::GetCommandIcon(Command command) { ResourceBundle& bundle = ResourceBundle::GetSharedInstance(); return bundle.GetImageNamed(GetCommandIconId(command)); @@ -194,7 +207,7 @@ } case LEARN_MORE_INTERRUPTED: GetBrowser()->OpenURL(content::OpenURLParams( - GURL(chrome::kDownloadInterruptedLearnMoreURL), content::Referrer(), + GetLearnMoreURLForInterruptedDownload(), content::Referrer(), NEW_FOREGROUND_TAB, ui::PAGE_TRANSITION_LINK, false)); break; case PAUSE:
diff --git a/chrome/browser/download/download_commands.h b/chrome/browser/download/download_commands.h index d6849b4..3ecfa08 100644 --- a/chrome/browser/download/download_commands.h +++ b/chrome/browser/download/download_commands.h
@@ -5,8 +5,8 @@ #ifndef CHROME_BROWSER_DOWNLOAD_DOWNLOAD_COMMANDS_H_ #define CHROME_BROWSER_DOWNLOAD_DOWNLOAD_COMMANDS_H_ +#include "base/gtest_prod_util.h" #include "base/strings/string16.h" - #include "chrome/browser/ui/browser.h" #include "content/public/browser/download_item.h" #include "content/public/browser/page_navigator.h" @@ -47,11 +47,13 @@ #endif private: + FRIEND_TEST_ALL_PREFIXES( + DownloadCommandsTest, + GetLearnMoreURLForInterruptedDownload_ContainsContext); + Browser* GetBrowser() const; - - int GetCommandIconId(Command command); - - int GetAlwaysOpenStringId() const; + int GetCommandIconId(Command command) const; + GURL GetLearnMoreURLForInterruptedDownload() const; content::DownloadItem* const download_item_; };
diff --git a/chrome/browser/download/download_commands_unittest.cc b/chrome/browser/download/download_commands_unittest.cc index 46717c4..e21770d 100644 --- a/chrome/browser/download/download_commands_unittest.cc +++ b/chrome/browser/download/download_commands_unittest.cc
@@ -6,6 +6,7 @@ #include <vector> +#include "base/strings/stringprintf.h" #include "content/public/test/mock_download_item.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -154,3 +155,15 @@ EXPECT_CALL(item(), Resume()).Times(1); commands().ExecuteCommand(DownloadCommands::RESUME); } + +TEST_F(DownloadCommandsTest, + GetLearnMoreURLForInterruptedDownload_ContainsContext) { + EXPECT_CALL(item(), GetLastReason()) + .WillOnce( + Return(content::DOWNLOAD_INTERRUPT_REASON_NETWORK_DISCONNECTED)); + GURL learn_more_url = commands().GetLearnMoreURLForInterruptedDownload(); + std::string name_value_pair = base::StringPrintf( + "ctx=%d", content::DOWNLOAD_INTERRUPT_REASON_NETWORK_DISCONNECTED); + EXPECT_LT(0u, learn_more_url.query().find(name_value_pair)) + << learn_more_url.spec(); +}
diff --git a/chrome/browser/engagement/OWNERS b/chrome/browser/engagement/OWNERS new file mode 100644 index 0000000..86e271a --- /dev/null +++ b/chrome/browser/engagement/OWNERS
@@ -0,0 +1,2 @@ +benwells@chromium.org +raymes@chromium.org
diff --git a/chrome/browser/engagement/site_engagement_helper.cc b/chrome/browser/engagement/site_engagement_helper.cc new file mode 100644 index 0000000..dfc9e57 --- /dev/null +++ b/chrome/browser/engagement/site_engagement_helper.cc
@@ -0,0 +1,44 @@ +// 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 "chrome/browser/engagement/site_engagement_helper.h" + +#include "chrome/browser/engagement/site_engagement_service.h" +#include "chrome/browser/engagement/site_engagement_service_factory.h" +#include "chrome/browser/prerender/prerender_contents.h" +#include "chrome/browser/profiles/profile.h" +#include "content/public/browser/navigation_entry.h" +#include "content/public/browser/web_contents.h" + +DEFINE_WEB_CONTENTS_USER_DATA_KEY(SiteEngagementHelper); + +SiteEngagementHelper::~SiteEngagementHelper() { +} + +SiteEngagementHelper::SiteEngagementHelper(content::WebContents* web_contents) + : content::WebContentsObserver(web_contents) { +} + +void SiteEngagementHelper::DidStartNavigationToPendingEntry( + const GURL& url, + content::NavigationController::ReloadType reload_type) { + prerender::PrerenderContents* prerender_contents = + prerender::PrerenderContents::FromWebContents(web_contents()); + + // Ignore pre-render loads. + if (prerender_contents != NULL) + return; + + // Ignore all schemes except HTTP and HTTPS. + if (!url.SchemeIsHTTPOrHTTPS()) + return; + + Profile* profile = + Profile::FromBrowserContext(web_contents()->GetBrowserContext()); + SiteEngagementService* service = + SiteEngagementServiceFactory::GetForProfile(profile); + DCHECK(service); + + service->HandleNavigation(url); +}
diff --git a/chrome/browser/engagement/site_engagement_helper.h b/chrome/browser/engagement/site_engagement_helper.h new file mode 100644 index 0000000..c3883d5 --- /dev/null +++ b/chrome/browser/engagement/site_engagement_helper.h
@@ -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. + +#ifndef CHROME_BROWSER_ENGAGEMENT_SITE_ENGAGEMENT_HELPER_H_ +#define CHROME_BROWSER_ENGAGEMENT_SITE_ENGAGEMENT_HELPER_H_ + +#include "base/macros.h" +#include "content/public/browser/web_contents_observer.h" +#include "content/public/browser/web_contents_user_data.h" + +namespace content { +class WebContents; +} + +class GURL; + +// Per-WebContents class to handle updating the site engagement scores for +// origins as the user navigates. +class SiteEngagementHelper + : public content::WebContentsObserver, + public content::WebContentsUserData<SiteEngagementHelper> { + public: + ~SiteEngagementHelper() override; + + private: + explicit SiteEngagementHelper(content::WebContents* web_contents); + friend class content::WebContentsUserData<SiteEngagementHelper>; + + // content::WebContentsObserver overrides. + void DidStartNavigationToPendingEntry( + const GURL& url, + content::NavigationController::ReloadType reload_type) override; + + DISALLOW_COPY_AND_ASSIGN(SiteEngagementHelper); +}; + +#endif // CHROME_BROWSER_ENGAGEMENT_SITE_ENGAGEMENT_HELPER_H_
diff --git a/chrome/browser/engagement/site_engagement_service.cc b/chrome/browser/engagement/site_engagement_service.cc new file mode 100644 index 0000000..d77fc6b --- /dev/null +++ b/chrome/browser/engagement/site_engagement_service.cc
@@ -0,0 +1,42 @@ +// 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 "chrome/browser/engagement/site_engagement_service.h" + +#include <algorithm> + +#include "base/command_line.h" +#include "chrome/browser/engagement/site_engagement_helper.h" +#include "chrome/browser/engagement/site_engagement_service_factory.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/common/chrome_switches.h" +#include "url/gurl.h" + +// static +SiteEngagementService* SiteEngagementService::Get(Profile* profile) { + return SiteEngagementServiceFactory::GetForProfile(profile); +} + +// static +bool SiteEngagementService::IsEnabled() { + return base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kEnableSiteEngagementService); +} + +SiteEngagementService::SiteEngagementService(Profile* profile) + : profile_(profile) { +} + +SiteEngagementService::~SiteEngagementService() { +} + +void SiteEngagementService::HandleNavigation(const GURL& url) { + GURL origin = url.GetOrigin(); + scores_[origin] = scores_[origin] + 1; +} + +int SiteEngagementService::GetScore(const GURL& url) { + return scores_[url.GetOrigin()]; +} +
diff --git a/chrome/browser/engagement/site_engagement_service.h b/chrome/browser/engagement/site_engagement_service.h new file mode 100644 index 0000000..bc150af --- /dev/null +++ b/chrome/browser/engagement/site_engagement_service.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 CHROME_BROWSER_ENGAGEMENT_SITE_ENGAGEMENT_SERVICE_H_ +#define CHROME_BROWSER_ENGAGEMENT_SITE_ENGAGEMENT_SERVICE_H_ + +#include <map> + +#include "base/macros.h" +#include "components/keyed_service/core/keyed_service.h" + +class GURL; +class Profile; + +// Stores and retrieves the engagement score of an origin. +// +// An engagement score is a positive integer that represents how much a user has +// engaged with an origin - the higher it is, the more engagement the user has +// had with this site recently. +// +// Positive user activity, such as visiting the origin often and adding it to +// the homescreen, will increase the site engagement score. Negative activity, +// such as rejecting permission prompts or not responding to notifications, will +// decrease the site engagement score. +class SiteEngagementService : public KeyedService { + public: + static SiteEngagementService* Get(Profile* profile); + + // Returns whether or not the SiteEngagementService is enabled. + static bool IsEnabled(); + + explicit SiteEngagementService(Profile* profile); + ~SiteEngagementService() override; + + // Update the karma score of the origin matching |url| for user navigation. + void HandleNavigation(const GURL& url); + + // Returns a non-negative integer representing the engagement score of the + // origin for this URL. + int GetScore(const GURL& url); + + private: + Profile* profile_; + + // Temporary non-persistent score database for testing. + std::map<GURL, int> scores_; + + DISALLOW_COPY_AND_ASSIGN(SiteEngagementService); +}; + +#endif // CHROME_BROWSER_ENGAGEMENT_SITE_ENGAGEMENT_SERVICE_H_
diff --git a/chrome/browser/engagement/site_engagement_service_factory.cc b/chrome/browser/engagement/site_engagement_service_factory.cc new file mode 100644 index 0000000..ce9070a --- /dev/null +++ b/chrome/browser/engagement/site_engagement_service_factory.cc
@@ -0,0 +1,39 @@ +// 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 "chrome/browser/engagement/site_engagement_service_factory.h" + +#include "chrome/browser/engagement/site_engagement_service.h" +#include "chrome/browser/profiles/profile.h" +#include "components/keyed_service/content/browser_context_dependency_manager.h" + +// static +SiteEngagementService* SiteEngagementServiceFactory::GetForProfile( + Profile* profile) { + return static_cast<SiteEngagementService*>( + GetInstance()->GetServiceForBrowserContext(profile, true)); +} + +// static +SiteEngagementServiceFactory* SiteEngagementServiceFactory::GetInstance() { + return Singleton<SiteEngagementServiceFactory>::get(); +} + +SiteEngagementServiceFactory::SiteEngagementServiceFactory() + : BrowserContextKeyedServiceFactory( + "SiteEngagementService", + BrowserContextDependencyManager::GetInstance()) { +} + +SiteEngagementServiceFactory::~SiteEngagementServiceFactory() { +} + +bool SiteEngagementServiceFactory::ServiceIsNULLWhileTesting() const { + return true; +} + +KeyedService* SiteEngagementServiceFactory::BuildServiceInstanceFor( + content::BrowserContext* profile) const { + return new SiteEngagementService(static_cast<Profile*>(profile)); +}
diff --git a/chrome/browser/engagement/site_engagement_service_factory.h b/chrome/browser/engagement/site_engagement_service_factory.h new file mode 100644 index 0000000..ccec13a --- /dev/null +++ b/chrome/browser/engagement/site_engagement_service_factory.h
@@ -0,0 +1,44 @@ +// 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 CHROME_BROWSER_ENGAGEMENT_SITE_ENGAGEMENT_SERVICE_FACTORY_H_ +#define CHROME_BROWSER_ENGAGEMENT_SITE_ENGAGEMENT_SERVICE_FACTORY_H_ + +#include "base/memory/singleton.h" +#include "components/keyed_service/content/browser_context_keyed_service_factory.h" + +class Profile; +class SiteEngagementService; + +// Singleton that owns all SiteEngagementServices and associates them with +// Profiles. Listens for the Profile's destruction notification and cleans up +// the associated SiteEngagementService. +// +// The default factory behavior is suitable for this factory as: +// * there should be no site engagement tracking in incognito +// * the site engagement service should be created lazily +// * the site engagement service is needed in tests. +class SiteEngagementServiceFactory : public BrowserContextKeyedServiceFactory { + public: + static SiteEngagementService* GetForProfile(Profile* profile); + + static SiteEngagementServiceFactory* GetInstance(); + + private: + friend struct DefaultSingletonTraits<SiteEngagementServiceFactory>; + + SiteEngagementServiceFactory(); + ~SiteEngagementServiceFactory() override; + + // KeyedServiceBaseFactory: + bool ServiceIsNULLWhileTesting() const override; + + // BrowserContextKeyedServiceFactory: + KeyedService* BuildServiceInstanceFor( + content::BrowserContext* profile) const override; + + DISALLOW_COPY_AND_ASSIGN(SiteEngagementServiceFactory); +}; + +#endif // CHROME_BROWSER_ENGAGEMENT_SITE_ENGAGEMENT_SERVICE_FACTORY_H_
diff --git a/chrome/browser/engagement/site_engagement_service_unittest.cc b/chrome/browser/engagement/site_engagement_service_unittest.cc new file mode 100644 index 0000000..baa1886 --- /dev/null +++ b/chrome/browser/engagement/site_engagement_service_unittest.cc
@@ -0,0 +1,33 @@ +// 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/command_line.h" +#include "chrome/browser/engagement/site_engagement_helper.h" +#include "chrome/browser/engagement/site_engagement_service.h" +#include "chrome/browser/engagement/site_engagement_service_factory.h" +#include "chrome/common/chrome_switches.h" +#include "chrome/test/base/browser_with_test_window_test.h" + +using SiteEngagementServiceTest = BrowserWithTestWindowTest; + +// Tests that the Site Engagement score is initially 0, and increments by 1 on +// each page request. +TEST_F(SiteEngagementServiceTest, ScoreIncrementsOnPageRequest) { + base::CommandLine::ForCurrentProcess()->AppendSwitch( + switches::kEnableSiteEngagementService); + + SiteEngagementService* service = + SiteEngagementServiceFactory::GetForProfile(profile()); + DCHECK(service); + + GURL url("http://www.google.com/"); + + AddTab(browser(), GURL("about:blank")); + EXPECT_EQ(0, service->GetScore(url)); + + for (int i = 0; i < 10; ++i) { + NavigateAndCommitActiveTab(url); + EXPECT_EQ(i + 1, service->GetScore(url)); + } +}
diff --git a/chrome/browser/extensions/api/developer_private/developer_private_api.cc b/chrome/browser/extensions/api/developer_private/developer_private_api.cc index 2d7d367..47e5e93 100644 --- a/chrome/browser/extensions/api/developer_private/developer_private_api.cc +++ b/chrome/browser/extensions/api/developer_private/developer_private_api.cc
@@ -245,11 +245,13 @@ error_console_observer_(this), process_manager_observer_(this), app_window_registry_observer_(this), + extension_action_api_observer_(this), profile_(profile) { extension_registry_observer_.Add(ExtensionRegistry::Get(profile_)); error_console_observer_.Add(ErrorConsole::Get(profile)); process_manager_observer_.Add(ProcessManager::Get(profile)); app_window_registry_observer_.Add(AppWindowRegistry::Get(profile)); + extension_action_api_observer_.Add(ExtensionActionAPI::Get(profile)); } DeveloperPrivateEventRouter::~DeveloperPrivateEventRouter() { @@ -338,6 +340,13 @@ window->extension_id()); } +void DeveloperPrivateEventRouter::OnExtensionActionVisibilityChanged( + const std::string& extension_id, + bool is_now_visible) { + BroadcastItemStateChanged( + profile_, developer::EVENT_TYPE_PREFS_CHANGED, extension_id); +} + void DeveloperPrivateAPI::SetLastUnpackedDirectory(const base::FilePath& path) { last_unpacked_directory_ = path; } @@ -605,8 +614,7 @@ extension->id(), browser_context(), *update.run_on_all_urls); } if (update.show_action_button) { - ExtensionActionAPI::SetBrowserActionVisibility( - ExtensionPrefs::Get(browser_context()), + ExtensionActionAPI::Get(browser_context())->SetBrowserActionVisibility( extension->id(), *update.show_action_button); }
diff --git a/chrome/browser/extensions/api/developer_private/developer_private_api.h b/chrome/browser/extensions/api/developer_private/developer_private_api.h index fea6e72..8e2e013a 100644 --- a/chrome/browser/extensions/api/developer_private/developer_private_api.h +++ b/chrome/browser/extensions/api/developer_private/developer_private_api.h
@@ -10,6 +10,7 @@ #include "base/files/file.h" #include "base/scoped_observer.h" #include "chrome/browser/extensions/api/developer_private/entry_picker.h" +#include "chrome/browser/extensions/api/extension_action/extension_action_api.h" #include "chrome/browser/extensions/api/file_system/file_system_api.h" #include "chrome/browser/extensions/chrome_extension_function.h" #include "chrome/browser/extensions/error_console/error_console.h" @@ -65,7 +66,8 @@ class DeveloperPrivateEventRouter : public ExtensionRegistryObserver, public ErrorConsole::Observer, public ProcessManagerObserver, - public AppWindowRegistry::Observer { + public AppWindowRegistry::Observer, + public ExtensionActionAPI::Observer { public: explicit DeveloperPrivateEventRouter(Profile* profile); ~DeveloperPrivateEventRouter() override; @@ -105,6 +107,10 @@ void OnAppWindowAdded(AppWindow* window) override; void OnAppWindowRemoved(AppWindow* window) override; + // ExtensionActionAPI::Observer: + void OnExtensionActionVisibilityChanged(const std::string& extension_id, + bool is_now_visible) override; + ScopedObserver<ExtensionRegistry, ExtensionRegistryObserver> extension_registry_observer_; ScopedObserver<ErrorConsole, ErrorConsole::Observer> error_console_observer_; @@ -112,6 +118,8 @@ process_manager_observer_; ScopedObserver<AppWindowRegistry, AppWindowRegistry::Observer> app_window_registry_observer_; + ScopedObserver<ExtensionActionAPI, ExtensionActionAPI::Observer> + extension_action_api_observer_; Profile* profile_;
diff --git a/chrome/browser/extensions/api/developer_private/extension_info_generator.cc b/chrome/browser/extensions/api/developer_private/extension_info_generator.cc index 562da65..9ac1c43 100644 --- a/chrome/browser/extensions/api/developer_private/extension_info_generator.cc +++ b/chrome/browser/extensions/api/developer_private/extension_info_generator.cc
@@ -140,6 +140,7 @@ : browser_context_(browser_context), extension_system_(ExtensionSystem::Get(browser_context)), extension_prefs_(ExtensionPrefs::Get(browser_context)), + extension_action_api_(ExtensionActionAPI::Get(browser_context)), warning_service_(WarningService::Get(browser_context)), error_console_(ErrorConsole::Get(browser_context)) { } @@ -155,8 +156,7 @@ // Don't consider the button hidden with the redesign, because "hidden" // buttons are now just hidden in the wrench menu. info->action_button_hidden = - !ExtensionActionAPI::GetBrowserActionVisibility( - extension_prefs_, extension.id()) && + !extension_action_api_->GetBrowserActionVisibility(extension.id()) && !FeatureSwitch::extension_action_redesign()->IsEnabled(); // Blacklist text.
diff --git a/chrome/browser/extensions/api/developer_private/extension_info_generator.h b/chrome/browser/extensions/api/developer_private/extension_info_generator.h index 736acce..29dc73ea 100644 --- a/chrome/browser/extensions/api/developer_private/extension_info_generator.h +++ b/chrome/browser/extensions/api/developer_private/extension_info_generator.h
@@ -14,6 +14,7 @@ namespace extensions { class ErrorConsole; class Extension; +class ExtensionActionAPI; class ExtensionPrefs; class ExtensionSystem; class WarningService; @@ -46,6 +47,7 @@ content::BrowserContext* browser_context_; ExtensionSystem* extension_system_; ExtensionPrefs* extension_prefs_; + ExtensionActionAPI* extension_action_api_; WarningService* warning_service_; ErrorConsole* error_console_;
diff --git a/chrome/browser/extensions/api/easy_unlock_private/easy_unlock_private_api.cc b/chrome/browser/extensions/api/easy_unlock_private/easy_unlock_private_api.cc index bb99a6a1..578592240 100644 --- a/chrome/browser/extensions/api/easy_unlock_private/easy_unlock_private_api.cc +++ b/chrome/browser/extensions/api/easy_unlock_private/easy_unlock_private_api.cc
@@ -461,7 +461,9 @@ base::Bind( &EasyUnlockPrivateSeekBluetoothDeviceByAddressFunction::OnSeekFailure, this), - content::BrowserThread::GetBlockingPool()); + content::BrowserThread::GetBlockingPool()-> + GetTaskRunnerWithShutdownBehavior( + base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN).get()); return true; }
diff --git a/chrome/browser/extensions/api/enterprise_platform_keys_private/enterprise_platform_keys_private_api_unittest.cc b/chrome/browser/extensions/api/enterprise_platform_keys_private/enterprise_platform_keys_private_api_unittest.cc index d85b6ad6..3f64a3f 100644 --- a/chrome/browser/extensions/api/enterprise_platform_keys_private/enterprise_platform_keys_private_api_unittest.cc +++ b/chrome/browser/extensions/api/enterprise_platform_keys_private/enterprise_platform_keys_private_api_unittest.cc
@@ -13,9 +13,10 @@ #include "base/strings/stringprintf.h" #include "base/values.h" #include "chrome/browser/chromeos/policy/stub_enterprise_install_attributes.h" -#include "chrome/browser/chromeos/settings/stub_cros_settings_provider.h" +#include "chrome/browser/chromeos/settings/scoped_cros_settings_test_helper.h" #include "chrome/browser/extensions/extension_function_test_utils.h" #include "chrome/browser/signin/signin_manager_factory.h" +#include "chrome/browser/ui/browser.h" #include "chrome/common/pref_names.h" #include "chrome/test/base/browser_with_test_window_test.h" #include "chromeos/attestation/attestation_constants.h" @@ -24,7 +25,6 @@ #include "chromeos/cryptohome/mock_async_method_caller.h" #include "chromeos/dbus/dbus_method_call_status.h" #include "chromeos/dbus/mock_cryptohome_client.h" -#include "chromeos/settings/cros_settings_provider.h" #include "components/policy/core/common/cloud/cloud_policy_constants.h" #include "components/signin/core/browser/signin_manager.h" #include "extensions/common/test_util.h" @@ -141,7 +141,8 @@ class EPKPChallengeKeyTestBase : public BrowserWithTestWindowTest { protected: - EPKPChallengeKeyTestBase() : extension_(test_util::CreateEmptyExtension()) { + EPKPChallengeKeyTestBase() + : settings_helper_(false), extension_(test_util::CreateEmptyExtension()) { // Set up the default behavior of mocks. ON_CALL(mock_cryptohome_client_, TpmAttestationDoesKeyExist(_, _, _, _)) .WillByDefault(WithArgs<3>(Invoke(FakeBoolDBusMethod( @@ -163,25 +164,8 @@ stub_install_attributes_.SetDeviceId("device_id"); stub_install_attributes_.SetMode(policy::DEVICE_MODE_ENTERPRISE); - // Replace the default device setting provider with the stub. - device_settings_provider_ = chromeos::CrosSettings::Get()->GetProvider( - chromeos::kReportDeviceVersionInfo); - EXPECT_TRUE(device_settings_provider_ != NULL); - EXPECT_TRUE(chromeos::CrosSettings::Get()-> - RemoveSettingsProvider(device_settings_provider_)); - chromeos::CrosSettings::Get()-> - AddSettingsProvider(&stub_settings_provider_); - - // Set the device settings. - stub_settings_provider_.Set(chromeos::kDeviceAttestationEnabled, - base::FundamentalValue(true)); - } - - virtual ~EPKPChallengeKeyTestBase() { - EXPECT_TRUE(chromeos::CrosSettings::Get()-> - RemoveSettingsProvider(&stub_settings_provider_)); - chromeos::CrosSettings::Get()-> - AddSettingsProvider(device_settings_provider_); + settings_helper_.ReplaceProvider(chromeos::kDeviceAttestationEnabled); + settings_helper_.SetBoolean(chromeos::kDeviceAttestationEnabled, true); } virtual void SetUp() override { @@ -206,10 +190,9 @@ NiceMock<chromeos::MockCryptohomeClient> mock_cryptohome_client_; NiceMock<cryptohome::MockAsyncMethodCaller> mock_async_method_caller_; NiceMock<chromeos::attestation::MockAttestationFlow> mock_attestation_flow_; + chromeos::ScopedCrosSettingsTestHelper settings_helper_; scoped_refptr<extensions::Extension> extension_; policy::StubEnterpriseInstallAttributes stub_install_attributes_; - chromeos::CrosSettingsProvider* device_settings_provider_; - chromeos::StubCrosSettingsProvider stub_settings_provider_; PrefService* prefs_; }; @@ -260,8 +243,7 @@ } TEST_F(EPKPChallengeMachineKeyTest, DevicePolicyDisabled) { - stub_settings_provider_.Set(chromeos::kDeviceAttestationEnabled, - base::FundamentalValue(false)); + settings_helper_.SetBoolean(chromeos::kDeviceAttestationEnabled, false); EXPECT_EQ(EPKPChallengeKeyBase::kDevicePolicyDisabledError, utils::RunFunctionAndReturnError(func_.get(), kArgs, browser())); @@ -397,8 +379,7 @@ } TEST_F(EPKPChallengeUserKeyTest, DevicePolicyDisabled) { - stub_settings_provider_.Set(chromeos::kDeviceAttestationEnabled, - base::FundamentalValue(false)); + settings_helper_.SetBoolean(chromeos::kDeviceAttestationEnabled, false); EXPECT_EQ(EPKPChallengeKeyBase::kDevicePolicyDisabledError, utils::RunFunctionAndReturnError(func_.get(), kArgs, browser()));
diff --git a/chrome/browser/extensions/api/extension_action/extension_action_api.cc b/chrome/browser/extensions/api/extension_action/extension_action_api.cc index 58f765b..c00dbc4 100644 --- a/chrome/browser/extensions/api/extension_action/extension_action_api.cc +++ b/chrome/browser/extensions/api/extension_action/extension_action_api.cc
@@ -63,6 +63,11 @@ content::BrowserContext* browser_context) { } +void ExtensionActionAPI::Observer::OnExtensionActionVisibilityChanged( + const std::string& extension_id, + bool is_now_visible) { +} + void ExtensionActionAPI::Observer::OnPageActionsUpdated( content::WebContents* web_contents) { } @@ -81,7 +86,8 @@ g_factory = LAZY_INSTANCE_INITIALIZER; ExtensionActionAPI::ExtensionActionAPI(content::BrowserContext* context) - : browser_context_(context) { + : browser_context_(context), + extension_prefs_(nullptr) { ExtensionFunctionRegistry* registry = ExtensionFunctionRegistry::GetInstance(); @@ -123,11 +129,18 @@ return BrowserContextKeyedAPIFactory<ExtensionActionAPI>::Get(context); } -// static +void ExtensionActionAPI::AddObserver(Observer* observer) { + observers_.AddObserver(observer); +} + +void ExtensionActionAPI::RemoveObserver(Observer* observer) { + observers_.RemoveObserver(observer); +} + bool ExtensionActionAPI::GetBrowserActionVisibility( - const ExtensionPrefs* prefs, const std::string& extension_id) { bool visible = false; + ExtensionPrefs* prefs = GetExtensionPrefs(); if (!prefs || !prefs->ReadPrefAsBoolean(extension_id, kBrowserActionVisible, &visible)) { @@ -136,29 +149,17 @@ return visible; } -// static void ExtensionActionAPI::SetBrowserActionVisibility( - ExtensionPrefs* prefs, const std::string& extension_id, bool visible) { - if (GetBrowserActionVisibility(prefs, extension_id) == visible) + if (GetBrowserActionVisibility(extension_id) == visible) return; - prefs->UpdateExtensionPref(extension_id, - kBrowserActionVisible, - new base::FundamentalValue(visible)); - content::NotificationService::current()->Notify( - NOTIFICATION_EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED, - content::Source<ExtensionPrefs>(prefs), - content::Details<const std::string>(&extension_id)); -} - -void ExtensionActionAPI::AddObserver(Observer* observer) { - observers_.AddObserver(observer); -} - -void ExtensionActionAPI::RemoveObserver(Observer* observer) { - observers_.RemoveObserver(observer); + GetExtensionPrefs()->UpdateExtensionPref(extension_id, + kBrowserActionVisible, + new base::FundamentalValue(visible)); + FOR_EACH_OBSERVER(Observer, observers_, OnExtensionActionVisibilityChanged( + extension_id, visible)); } ExtensionAction::ShowAction ExtensionActionAPI::ExecuteExtensionAction( @@ -284,6 +285,15 @@ } } +ExtensionPrefs* ExtensionActionAPI::GetExtensionPrefs() { + // This lazy initialization is more than just an optimization, because it + // allows tests to associate a new ExtensionPrefs with the browser context + // before we access it. + if (!extension_prefs_) + extension_prefs_ = ExtensionPrefs::Get(browser_context_); + return extension_prefs_; +} + void ExtensionActionAPI::DispatchEventToExtension( content::BrowserContext* context, const std::string& extension_id,
diff --git a/chrome/browser/extensions/api/extension_action/extension_action_api.h b/chrome/browser/extensions/api/extension_action/extension_action_api.h index 5a1f6ce..63dccbd5 100644 --- a/chrome/browser/extensions/api/extension_action/extension_action_api.h +++ b/chrome/browser/extensions/api/extension_action/extension_action_api.h
@@ -42,6 +42,11 @@ content::WebContents* web_contents, content::BrowserContext* browser_context); + // Called when there is a change to the extension action's visibility. + virtual void OnExtensionActionVisibilityChanged( + const std::string& extension_id, + bool is_now_visible); + // Called when the page actions have been refreshed do to a possible change // in count or visibility. virtual void OnPageActionsUpdated(content::WebContents* web_contents); @@ -60,13 +65,6 @@ // Convenience method to get the instance for a profile. static ExtensionActionAPI* Get(content::BrowserContext* context); - static bool GetBrowserActionVisibility(const ExtensionPrefs* prefs, - const std::string& extension_id); - static void SetBrowserActionVisibility(ExtensionPrefs* prefs, - const std::string& extension_id, - bool visible); - - // BrowserContextKeyedAPI implementation. static BrowserContextKeyedAPIFactory<ExtensionActionAPI>* GetFactoryInstance(); @@ -74,6 +72,11 @@ void AddObserver(Observer* observer); void RemoveObserver(Observer* observer); + bool GetBrowserActionVisibility(const std::string& extension_id); + void SetBrowserActionVisibility(const std::string& extension_id, + bool visible); + + // BrowserContextKeyedAPI implementation. // Executes the action of the given |extension| on the |browser|'s active // web contents. If |grant_tab_permissions| is true, this will also grant // activeTab to the extension (so this should only be done if this is through @@ -109,9 +112,16 @@ // changed, and signals the browser to update. void NotifyPageActionsChanged(content::WebContents* web_contents); + void set_prefs_for_testing(ExtensionPrefs* prefs) { + extension_prefs_ = prefs; + } + private: friend class BrowserContextKeyedAPIFactory<ExtensionActionAPI>; + // Returns the associated extension prefs. + ExtensionPrefs* GetExtensionPrefs(); + // The DispatchEvent methods forward events to the |context|'s event router. void DispatchEventToExtension(content::BrowserContext* context, const std::string& extension_id, @@ -132,6 +142,8 @@ content::BrowserContext* browser_context_; + ExtensionPrefs* extension_prefs_; + DISALLOW_COPY_AND_ASSIGN(ExtensionActionAPI); };
diff --git a/chrome/browser/extensions/api/extension_action/extension_action_prefs_unittest.cc b/chrome/browser/extensions/api/extension_action/extension_action_prefs_unittest.cc index b69506a1..6925b98e 100644 --- a/chrome/browser/extensions/api/extension_action/extension_action_prefs_unittest.cc +++ b/chrome/browser/extensions/api/extension_action/extension_action_prefs_unittest.cc
@@ -9,6 +9,7 @@ #include "base/strings/stringprintf.h" #include "chrome/browser/extensions/api/extension_action/extension_action_api.h" #include "chrome/browser/extensions/extension_prefs_unittest.h" +#include "chrome/test/base/testing_profile.h" #include "extensions/common/extension.h" namespace extensions { @@ -16,42 +17,47 @@ // Tests force hiding browser actions. class ExtensionPrefsHidingBrowserActions : public ExtensionPrefsTest { public: + ExtensionPrefsHidingBrowserActions() {} + ~ExtensionPrefsHidingBrowserActions() override {} + void Initialize() override { + profile_.reset(new TestingProfile()); + // Install 5 extensions. for (int i = 0; i < 5; i++) { std::string name = "test" + base::IntToString(i); extensions_.push_back(prefs_.AddExtension(name)); } - ExtensionList::const_iterator iter; - for (iter = extensions_.begin(); iter != extensions_.end(); ++iter) { - EXPECT_TRUE(ExtensionActionAPI::GetBrowserActionVisibility( - prefs(), (*iter)->id())); - } + ExtensionActionAPI* action_api = ExtensionActionAPI::Get(profile_.get()); + action_api->set_prefs_for_testing(prefs()); + for (const scoped_refptr<const Extension>& extension : extensions_) + EXPECT_TRUE(action_api->GetBrowserActionVisibility(extension->id())); - ExtensionActionAPI::SetBrowserActionVisibility( - prefs(), extensions_[0]->id(), false); - ExtensionActionAPI::SetBrowserActionVisibility( - prefs(), extensions_[1]->id(), true); + action_api->SetBrowserActionVisibility(extensions_[0]->id(), false); + action_api->SetBrowserActionVisibility(extensions_[1]->id(), true); } void Verify() override { + ExtensionActionAPI* action_api = ExtensionActionAPI::Get(profile_.get()); + action_api->set_prefs_for_testing(prefs()); // Make sure the one we hid is hidden. - EXPECT_FALSE(ExtensionActionAPI::GetBrowserActionVisibility( - prefs(), extensions_[0]->id())); + EXPECT_FALSE(action_api->GetBrowserActionVisibility(extensions_[0]->id())); // Make sure the other id's are not hidden. ExtensionList::const_iterator iter = extensions_.begin() + 1; for (; iter != extensions_.end(); ++iter) { SCOPED_TRACE(base::StringPrintf("Loop %d ", static_cast<int>(iter - extensions_.begin()))); - EXPECT_TRUE(ExtensionActionAPI::GetBrowserActionVisibility( - prefs(), (*iter)->id())); + EXPECT_TRUE(action_api->GetBrowserActionVisibility((*iter)->id())); } } private: + scoped_ptr<TestingProfile> profile_; ExtensionList extensions_; + + DISALLOW_COPY_AND_ASSIGN(ExtensionPrefsHidingBrowserActions); }; TEST_F(ExtensionPrefsHidingBrowserActions, ForceHide) {}
diff --git a/chrome/browser/extensions/api/input_ime/input_ime_api.cc b/chrome/browser/extensions/api/input_ime/input_ime_api.cc index 77d6fb5..db85ab7 100644 --- a/chrome/browser/extensions/api/input_ime/input_ime_api.cc +++ b/chrome/browser/extensions/api/input_ime/input_ime_api.cc
@@ -199,6 +199,10 @@ args.Pass()); } + bool IsInterestedInKeyEvent() const override { + return ShouldForwardKeyEvent(); + } + void OnKeyEvent(const std::string& component_id, const InputMethodEngineInterface::KeyboardEvent& event, chromeos::input_method::KeyEventHandle* key_data) override {
diff --git a/chrome/browser/extensions/api/log_private/log_private_api_chromeos.cc b/chrome/browser/extensions/api/log_private/log_private_api_chromeos.cc index e53007b1..5423b480 100644 --- a/chrome/browser/extensions/api/log_private/log_private_api_chromeos.cc +++ b/chrome/browser/extensions/api/log_private/log_private_api_chromeos.cc
@@ -288,9 +288,10 @@ return; write_to_file_observer_.reset(new net::WriteToFileNetLogObserver()); - write_to_file_observer_->set_log_level(net::NetLog::LOG_ALL_BUT_BYTES); + write_to_file_observer_->set_capture_mode( + net::NetLogCaptureMode::IncludeCookiesAndCredentials()); write_to_file_observer_->StartObserving(io_thread->net_log(), file->Pass(), - nullptr, nullptr); + nullptr, nullptr); } void LogPrivateAPI::MaybeStartNetInternalLogging( @@ -304,7 +305,7 @@ switch (event_sink_) { case api::log_private::EVENT_SINK_CAPTURE: { io_thread->net_log()->DeprecatedAddObserver( - this, net::NetLog::LOG_ALL_BUT_BYTES); + this, net::NetLogCaptureMode::IncludeCookiesAndCredentials()); break; } case api::log_private::EVENT_SINK_FILE: {
diff --git a/chrome/browser/extensions/chrome_content_browser_client_extensions_part.cc b/chrome/browser/extensions/chrome_content_browser_client_extensions_part.cc index 91b71860..8f6cf682 100644 --- a/chrome/browser/extensions/chrome_content_browser_client_extensions_part.cc +++ b/chrome/browser/extensions/chrome_content_browser_client_extensions_part.cc
@@ -34,6 +34,7 @@ #include "extensions/browser/extension_message_filter.h" #include "extensions/browser/extension_registry.h" #include "extensions/browser/extension_system.h" +#include "extensions/browser/guest_view/extensions_guest_view_message_filter.h" #include "extensions/browser/guest_view/guest_view_message_filter.h" #include "extensions/browser/info_map.h" #include "extensions/browser/io_thread_extension_message_filter.h" @@ -378,6 +379,7 @@ host->AddFilter(new ChromeExtensionMessageFilter(id, profile)); host->AddFilter(new ExtensionMessageFilter(id, profile)); host->AddFilter(new IOThreadExtensionMessageFilter(id, profile)); + host->AddFilter(new ExtensionsGuestViewMessageFilter(id, profile)); host->AddFilter(new GuestViewMessageFilter(id, profile)); extension_web_request_api_helpers::SendExtensionWebRequestStatusToHost(host); }
diff --git a/chrome/browser/extensions/display_info_provider_chromeos.cc b/chrome/browser/extensions/display_info_provider_chromeos.cc index 60c6e6c..fda4b63 100644 --- a/chrome/browser/extensions/display_info_provider_chromeos.cc +++ b/chrome/browser/extensions/display_info_provider_chromeos.cc
@@ -318,7 +318,7 @@ // Process 'mirroringSourceId' parameter. if (info.mirroring_source_id && - info.mirroring_source_id->empty() == display_manager->IsMirrored()) { + info.mirroring_source_id->empty() == display_manager->IsInMirrorMode()) { display_controller->ToggleMirrorMode(); } @@ -361,9 +361,9 @@ ash::DisplayManager* display_manager = ash::Shell::GetInstance()->display_manager(); unit->name = display_manager->GetDisplayNameForId(display.id()); - if (display_manager->IsMirrored()) { + if (display_manager->IsInMirrorMode()) { unit->mirroring_source_id = - base::Int64ToString(display_manager->mirrored_display_id()); + base::Int64ToString(display_manager->mirroring_display_id()); } // TODO(hshi): determine the DPI of the screen.
diff --git a/chrome/browser/extensions/display_info_provider_chromeos_unittest.cc b/chrome/browser/extensions/display_info_provider_chromeos_unittest.cc index 493e54c..28d3eab 100644 --- a/chrome/browser/extensions/display_info_provider_chromeos_unittest.cc +++ b/chrome/browser/extensions/display_info_provider_chromeos_unittest.cc
@@ -253,12 +253,12 @@ ASSERT_TRUE(DisplayExists(display_id_secondary)) << display_id_secondary << " not found"; - ASSERT_FALSE(GetDisplayManager()->IsMirrored()); + ASSERT_FALSE(GetDisplayManager()->IsInMirrorMode()); EXPECT_TRUE(result[0]->mirroring_source_id.empty()); EXPECT_TRUE(result[1]->mirroring_source_id.empty()); GetDisplayManager()->SetMirrorMode(true); - ASSERT_TRUE(GetDisplayManager()->IsMirrored()); + ASSERT_TRUE(GetDisplayManager()->IsInMirrorMode()); result = DisplayInfoProvider::Get()->GetAllDisplaysInfo(); @@ -268,7 +268,7 @@ result[0]->mirroring_source_id); GetDisplayManager()->SetMirrorMode(false); - ASSERT_FALSE(GetDisplayManager()->IsMirrored()); + ASSERT_FALSE(GetDisplayManager()->IsInMirrorMode()); result = DisplayInfoProvider::Get()->GetAllDisplaysInfo();
diff --git a/chrome/browser/extensions/extension_context_menu_model.cc b/chrome/browser/extensions/extension_context_menu_model.cc index a54ac2c..f5b1409 100644 --- a/chrome/browser/extensions/extension_context_menu_model.cc +++ b/chrome/browser/extensions/extension_context_menu_model.cc
@@ -85,8 +85,8 @@ } else { // With the redesign, we display "show" or "hide" based on the icon's // visibility. - bool visible = ExtensionActionAPI::GetBrowserActionVisibility( - ExtensionPrefs::Get(profile), extension->id()); + bool visible = ExtensionActionAPI::Get(profile)->GetBrowserActionVisibility( + extension->id()); string_id = visible ? IDS_EXTENSIONS_HIDE_BUTTON : IDS_EXTENSIONS_SHOW_BUTTON; } @@ -206,11 +206,9 @@ extensions::ExtensionTabUtil::OpenOptionsPage(extension, browser_); break; case TOGGLE_VISIBILITY: { - ExtensionPrefs* prefs = ExtensionPrefs::Get(profile_); - bool visible = ExtensionActionAPI::GetBrowserActionVisibility( - prefs, extension->id()); - ExtensionActionAPI::SetBrowserActionVisibility( - prefs, extension->id(), !visible); + ExtensionActionAPI* api = ExtensionActionAPI::Get(profile_); + bool visible = api->GetBrowserActionVisibility(extension->id()); + api->SetBrowserActionVisibility(extension->id(), !visible); break; } case UNINSTALL: {
diff --git a/chrome/browser/extensions/extension_context_menu_model_unittest.cc b/chrome/browser/extensions/extension_context_menu_model_unittest.cc index 16fb6959..e1976f8 100644 --- a/chrome/browser/extensions/extension_context_menu_model_unittest.cc +++ b/chrome/browser/extensions/extension_context_menu_model_unittest.cc
@@ -16,7 +16,6 @@ #include "chrome/test/base/test_browser_window.h" #include "chrome/test/base/testing_profile.h" #include "components/crx_file/id_util.h" -#include "extensions/browser/extension_prefs.h" #include "extensions/browser/extension_system.h" #include "extensions/browser/test_management_policy.h" #include "extensions/common/extension_builder.h" @@ -262,7 +261,6 @@ l10n_util::GetStringUTF16(IDS_EXTENSIONS_HIDE_BUTTON); base::string16 show_string = l10n_util::GetStringUTF16(IDS_EXTENSIONS_SHOW_BUTTON); - ExtensionPrefs* prefs = ExtensionPrefs::Get(profile()); int index = GetCommandIndex(menu, visibility_command); // Without the toolbar redesign switch, page action menus shouldn't have a @@ -286,13 +284,13 @@ index = GetCommandIndex(menu, visibility_command); // By default, browser actions should be visible (and therefore the button // should be to hide). - EXPECT_TRUE(ExtensionActionAPI::GetBrowserActionVisibility( - prefs, browser_action->id())); + ExtensionActionAPI* extension_action_api = ExtensionActionAPI::Get(profile()); + EXPECT_TRUE(extension_action_api->GetBrowserActionVisibility( + browser_action->id())); EXPECT_EQ(hide_string, menu->GetLabelAt(index)); // Hide the browser action. This should mean the string is "show". - ExtensionActionAPI::SetBrowserActionVisibility( - prefs, browser_action->id(), false); + extension_action_api->SetBrowserActionVisibility(browser_action->id(), false); menu = new ExtensionContextMenuModel(browser_action.get(), browser.get()); index = GetCommandIndex(menu, visibility_command); EXPECT_NE(-1, index);
diff --git a/chrome/browser/extensions/extension_toolbar_model.cc b/chrome/browser/extensions/extension_toolbar_model.cc index 5a61ea6..085936b 100644 --- a/chrome/browser/extensions/extension_toolbar_model.cc +++ b/chrome/browser/extensions/extension_toolbar_model.cc
@@ -42,6 +42,7 @@ : profile_(profile), extension_prefs_(extension_prefs), prefs_(profile_->GetPrefs()), + extension_action_api_(ExtensionActionAPI::Get(profile_)), extensions_initialized_(false), include_all_extensions_( FeatureSwitch::extension_action_redesign()->IsEnabled()), @@ -160,6 +161,47 @@ } } +void ExtensionToolbarModel::OnExtensionActionVisibilityChanged( + const std::string& extension_id, + bool is_now_visible) { + const Extension* extension = + ExtensionRegistry::Get(profile_)->GetExtensionById( + extension_id, ExtensionRegistry::EVERYTHING); + + // Hiding works differently with the new and old toolbars. + if (include_all_extensions_) { + // It's possible that we haven't added this extension yet, if its + // visibility was adjusted in the course of its initialization. + if (std::find(toolbar_items_.begin(), toolbar_items_.end(), extension) == + toolbar_items_.end()) + return; + + int new_size = 0; + int new_index = 0; + if (is_now_visible) { + // If this action used to be hidden, we can't possibly be showing all. + DCHECK_LT(visible_icon_count(), toolbar_items_.size()); + // Grow the bar by one and move the extension to the end of the visibles. + new_size = visible_icon_count() + 1; + new_index = new_size - 1; + } else { + // If we're hiding one, we must be showing at least one. + DCHECK_GE(visible_icon_count(), 0u); + // Shrink the bar by one and move the extension to the beginning of the + // overflow menu. + new_size = visible_icon_count() - 1; + new_index = new_size; + } + SetVisibleIconCount(new_size); + MoveExtensionIcon(extension->id(), new_index); + } else { // Don't include all extensions. + if (is_now_visible) + AddExtension(extension); + else + RemoveExtension(extension); + } +} + void ExtensionToolbarModel::OnExtensionLoaded( content::BrowserContext* browser_context, const Extension* extension) { @@ -197,52 +239,6 @@ } } -void ExtensionToolbarModel::Observe( - int type, - const content::NotificationSource& source, - const content::NotificationDetails& details) { - DCHECK_EQ(NOTIFICATION_EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED, type); - const Extension* extension = - ExtensionRegistry::Get(profile_)->GetExtensionById( - *content::Details<const std::string>(details).ptr(), - ExtensionRegistry::EVERYTHING); - - bool visible = ExtensionActionAPI::GetBrowserActionVisibility( - extension_prefs_, extension->id()); - // Hiding works differently with the new and old toolbars. - if (include_all_extensions_) { - // It's possible that we haven't added this extension yet, if its - // visibility was adjusted in the course of its initialization. - if (std::find(toolbar_items_.begin(), toolbar_items_.end(), extension) == - toolbar_items_.end()) - return; - - int new_size = 0; - int new_index = 0; - if (visible) { - // If this action used to be hidden, we can't possibly be showing all. - DCHECK_LT(visible_icon_count(), toolbar_items_.size()); - // Grow the bar by one and move the extension to the end of the visibles. - new_size = visible_icon_count() + 1; - new_index = new_size - 1; - } else { - // If we're hiding one, we must be showing at least one. - DCHECK_GE(visible_icon_count(), 0u); - // Shrink the bar by one and move the extension to the beginning of the - // overflow menu. - new_size = visible_icon_count() - 1; - new_index = new_size; - } - SetVisibleIconCount(new_size); - MoveExtensionIcon(extension->id(), new_index); - } else { // Don't include all extensions. - if (visible) - AddExtension(extension); - else - RemoveExtension(extension); - } -} - void ExtensionToolbarModel::OnReady() { ExtensionRegistry* registry = ExtensionRegistry::Get(profile_); InitializeExtensionList(); @@ -250,11 +246,7 @@ // changes so that the toolbar buttons can be shown in their stable ordering // taken from prefs. extension_registry_observer_.Add(registry); - extension_action_observer_.Add(ExtensionActionAPI::Get(profile_)); - registrar_.Add( - this, - extensions::NOTIFICATION_EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED, - content::Source<ExtensionPrefs>(extension_prefs_)); + extension_action_observer_.Add(extension_action_api_); } size_t ExtensionToolbarModel::FindNewPositionFromLastKnownGood( @@ -299,8 +291,7 @@ } return action_manager->GetBrowserAction(*extension) && - ExtensionActionAPI::GetBrowserActionVisibility( - extension_prefs_, extension->id()); + extension_action_api_->GetBrowserActionVisibility(extension->id()); } void ExtensionToolbarModel::AddExtension(const Extension* extension) { @@ -476,8 +467,7 @@ int hidden = 0; for (const scoped_refptr<const Extension>& extension : extensions) { if (!ShouldAddExtension(extension.get())) { - if (!ExtensionActionAPI::GetBrowserActionVisibility(extension_prefs_, - extension->id())) + if (!extension_action_api_->GetBrowserActionVisibility(extension->id())) ++hidden; continue; } @@ -570,27 +560,18 @@ // overflow menu with the new toolbar design. if (include_all_extensions_ && !profile_->IsOffTheRecord()) { bool visible = index < visible_icon_count(); - if (visible != ExtensionActionAPI::GetBrowserActionVisibility( - extension_prefs_, extension->id())) { + if (visible != extension_action_api_->GetBrowserActionVisibility( + extension->id())) { // Don't observe changes caused by ourselves. bool was_registered = false; - if (registrar_.IsRegistered( - this, - NOTIFICATION_EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED, - content::Source<ExtensionPrefs>(extension_prefs_))) { + if (extension_action_observer_.IsObserving(extension_action_api_)) { was_registered = true; - registrar_.Remove( - this, - NOTIFICATION_EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED, - content::Source<ExtensionPrefs>(extension_prefs_)); + extension_action_observer_.RemoveAll(); } - ExtensionActionAPI::SetBrowserActionVisibility( - extension_prefs_, extension->id(), visible); - if (was_registered) { - registrar_.Add(this, - NOTIFICATION_EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED, - content::Source<ExtensionPrefs>(extension_prefs_)); - } + extension_action_api_->SetBrowserActionVisibility(extension->id(), + visible); + if (was_registered) + extension_action_observer_.Add(extension_action_api_); } } } @@ -720,9 +701,8 @@ if (visible_icon_count() < extension_ids.size()) SetVisibleIconCount(extension_ids.size()); - // It's important that is_highlighting_ is changed right immediately before - // the observers are notified since it changes the result of - // toolbar_items(). + // It's important that is_highlighting_ is changed immediately before the + // observers are notified since it changes the result of toolbar_items(). is_highlighting_ = true; FOR_EACH_OBSERVER(Observer, observers_, OnToolbarHighlightModeChanged(true)); @@ -738,16 +718,17 @@ void ExtensionToolbarModel::StopHighlighting() { if (is_highlighting_) { - highlighted_items_.clear(); if (old_visible_icon_count_ != visible_icon_count_) SetVisibleIconCount(old_visible_icon_count_); - // It's important that is_highlighting_ is changed right immediately before - // the observers are notified since it changes the result of - // toolbar_items(). + // It's important that is_highlighting_ is changed immediately before the + // observers are notified since it changes the result of toolbar_items(). is_highlighting_ = false; FOR_EACH_OBSERVER(Observer, observers_, OnToolbarHighlightModeChanged(false)); + // For the same reason, we don't clear highlighted_items_ until after the + // mode changed. + highlighted_items_.clear(); } }
diff --git a/chrome/browser/extensions/extension_toolbar_model.h b/chrome/browser/extensions/extension_toolbar_model.h index f3025b7..cc53ea8 100644 --- a/chrome/browser/extensions/extension_toolbar_model.h +++ b/chrome/browser/extensions/extension_toolbar_model.h
@@ -12,8 +12,6 @@ #include "chrome/browser/extensions/api/extension_action/extension_action_api.h" #include "chrome/browser/extensions/extension_action.h" #include "components/keyed_service/core/keyed_service.h" -#include "content/public/browser/notification_observer.h" -#include "content/public/browser/notification_registrar.h" #include "extensions/browser/extension_prefs.h" #include "extensions/browser/extension_registry_observer.h" #include "extensions/common/extension.h" @@ -27,8 +25,7 @@ class ExtensionSet; // Model for the browser actions toolbar. -class ExtensionToolbarModel : public content::NotificationObserver, - public ExtensionActionAPI::Observer, +class ExtensionToolbarModel : public ExtensionActionAPI::Observer, public ExtensionRegistryObserver, public KeyedService { public: @@ -160,11 +157,6 @@ bool RedesignIsShowingNewIcons() const; private: - // content::NotificationObserver: - void Observe(int type, - const content::NotificationSource& source, - const content::NotificationDetails& details) override; - // Callback when extensions are ready. void OnReady(); @@ -183,6 +175,8 @@ ExtensionAction* extension_action, content::WebContents* web_contents, content::BrowserContext* browser_context) override; + void OnExtensionActionVisibilityChanged(const std::string& extension_id, + bool is_now_visible) override; // To be called after the extension service is ready; gets loaded extensions // from the ExtensionRegistry and their saved order from the pref service @@ -223,6 +217,9 @@ ExtensionPrefs* extension_prefs_; PrefService* prefs_; + // The ExtensionActionAPI object, cached for convenience. + ExtensionActionAPI* extension_action_api_; + // True if we've handled the initial EXTENSIONS_READY notification. bool extensions_initialized_; @@ -255,8 +252,6 @@ // visible, instead of overloading this one. int visible_icon_count_; - content::NotificationRegistrar registrar_; - ScopedObserver<ExtensionActionAPI, ExtensionActionAPI::Observer> extension_action_observer_;
diff --git a/chrome/browser/extensions/extension_toolbar_model_unittest.cc b/chrome/browser/extensions/extension_toolbar_model_unittest.cc index 922dc493..90c36f5 100644 --- a/chrome/browser/extensions/extension_toolbar_model_unittest.cc +++ b/chrome/browser/extensions/extension_toolbar_model_unittest.cc
@@ -782,6 +782,8 @@ ExtensionToolbarActionsVisibilityNoSwitch) { Init(); + ExtensionActionAPI* action_api = ExtensionActionAPI::Get(profile()); + ASSERT_TRUE(AddBrowserActionExtensions()); // Sanity check: Order should start as A B C. EXPECT_EQ(3u, num_toolbar_items()); @@ -789,21 +791,15 @@ EXPECT_EQ(browser_action_b(), GetExtensionAtIndex(1u)); EXPECT_EQ(browser_action_c(), GetExtensionAtIndex(2u)); - ExtensionPrefs* prefs = ExtensionPrefs::Get(profile()); - // By default, all actions should be visible. - EXPECT_TRUE(ExtensionActionAPI::GetBrowserActionVisibility( - prefs, browser_action_a()->id())); - EXPECT_TRUE(ExtensionActionAPI::GetBrowserActionVisibility( - prefs, browser_action_b()->id())); - EXPECT_TRUE(ExtensionActionAPI::GetBrowserActionVisibility( - prefs, browser_action_c()->id())); + EXPECT_TRUE(action_api->GetBrowserActionVisibility(browser_action_a()->id())); + EXPECT_TRUE(action_api->GetBrowserActionVisibility(browser_action_b()->id())); + EXPECT_TRUE(action_api->GetBrowserActionVisibility(browser_action_c()->id())); // Hiding an action should result in its removal from the toolbar. - ExtensionActionAPI::SetBrowserActionVisibility( - prefs, browser_action_b()->id(), false); - EXPECT_FALSE(ExtensionActionAPI::GetBrowserActionVisibility( - prefs, browser_action_b()->id())); + action_api->SetBrowserActionVisibility(browser_action_b()->id(), false); + EXPECT_FALSE(action_api->GetBrowserActionVisibility( + browser_action_b()->id())); // Thus, there should now only be two items on the toolbar - A and C. EXPECT_EQ(2u, num_toolbar_items()); EXPECT_EQ(browser_action_a(), GetExtensionAtIndex(0u)); @@ -811,10 +807,8 @@ // Resetting the visibility to 'true' should result in the extension being // added back at its original position. - ExtensionActionAPI::SetBrowserActionVisibility( - prefs, browser_action_b()->id(), true); - EXPECT_TRUE(ExtensionActionAPI::GetBrowserActionVisibility( - prefs, browser_action_b()->id())); + action_api->SetBrowserActionVisibility(browser_action_b()->id(), true); + EXPECT_TRUE(action_api->GetBrowserActionVisibility(browser_action_b()->id())); // So the toolbar order should be A B C. EXPECT_EQ(3u, num_toolbar_items()); EXPECT_EQ(browser_action_a(), GetExtensionAtIndex(0u)); @@ -1013,19 +1007,15 @@ EXPECT_EQ(extension_b, GetExtensionAtIndex(1u)); EXPECT_EQ(extension_c, GetExtensionAtIndex(2u)); - ExtensionPrefs* prefs = ExtensionPrefs::Get(profile()); + ExtensionActionAPI* action_api = ExtensionActionAPI::Get(profile()); // By default, all actions should be visible. - EXPECT_TRUE(ExtensionActionAPI::GetBrowserActionVisibility( - prefs, extension_a->id())); - EXPECT_TRUE(ExtensionActionAPI::GetBrowserActionVisibility( - prefs, extension_c->id())); - EXPECT_TRUE(ExtensionActionAPI::GetBrowserActionVisibility( - prefs, extension_b->id())); + EXPECT_TRUE(action_api->GetBrowserActionVisibility(extension_a->id())); + EXPECT_TRUE(action_api->GetBrowserActionVisibility(extension_c->id())); + EXPECT_TRUE(action_api->GetBrowserActionVisibility(extension_b->id())); // Hiding an action should result in it being sent to the overflow menu. - ExtensionActionAPI::SetBrowserActionVisibility( - prefs, extension_b->id(), false); + action_api->SetBrowserActionVisibility(extension_b->id(), false); // Thus, the order should be A C B, with B in the overflow. EXPECT_EQ(3u, num_toolbar_items()); @@ -1036,8 +1026,7 @@ // Hiding an extension's action should result in it being sent to the overflow // as well, but as the _first_ extension in the overflow. - ExtensionActionAPI::SetBrowserActionVisibility( - prefs, extension_a->id(), false); + action_api->SetBrowserActionVisibility(extension_a->id(), false); // Thus, the order should be C A B, with A and B in the overflow. EXPECT_EQ(3u, num_toolbar_items()); EXPECT_EQ(1u, toolbar_model()->visible_icon_count()); @@ -1048,8 +1037,7 @@ // Resetting A's visibility to true should send it back to the visible icons // (and should grow visible icons by 1), but it should be added to the end of // the visible icon list (not to its original position). - ExtensionActionAPI::SetBrowserActionVisibility( - prefs, extension_a->id(), true); + action_api->SetBrowserActionVisibility(extension_a->id(), true); // So order is C A B, with only B in the overflow. EXPECT_EQ(3u, num_toolbar_items()); EXPECT_EQ(2u, toolbar_model()->visible_icon_count()); @@ -1058,8 +1046,7 @@ EXPECT_EQ(extension_b, GetExtensionAtIndex(2u)); // Resetting B to be visible should make the order C A B, with no overflow. - ExtensionActionAPI::SetBrowserActionVisibility( - prefs, extension_b->id(), true); + action_api->SetBrowserActionVisibility(extension_b->id(), true); EXPECT_EQ(3u, num_toolbar_items()); EXPECT_TRUE(toolbar_model()->all_icons_visible()); EXPECT_EQ(extension_c, GetExtensionAtIndex(0u));
diff --git a/chrome/browser/favicon/chrome_fallback_icon_client.cc b/chrome/browser/favicon/chrome_fallback_icon_client.cc index 29651d2..292625e9 100644 --- a/chrome/browser/favicon/chrome_fallback_icon_client.cc +++ b/chrome/browser/favicon/chrome_fallback_icon_client.cc
@@ -11,6 +11,10 @@ #include "ui/base/l10n/l10n_util.h" #include "url/gurl.h" +namespace { +const char* kFallbackIconTextForIP = "IP"; +} // namespace + ChromeFallbackIconClient::ChromeFallbackIconClient() { #if defined(OS_CHROMEOS) font_list_.push_back("Noto Sans"); @@ -33,9 +37,17 @@ // letter in a domain's name and make it upper case. base::string16 ChromeFallbackIconClient::GetFallbackIconText(const GURL& url) const { - // TODO(huangs): Handle non-ASCII ("xn--") domain names. + if (url.is_empty()) + return base::string16(); std::string domain = net::registry_controlled_domains::GetDomainAndRegistry( url, net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES); - return domain.empty() ? base::string16() : - base::i18n::ToUpper(base::ASCIIToUTF16(domain.substr(0, 1))); + if (domain.empty()) { // E.g., http://localhost/ or http://192.168.0.1/ + if (url.HostIsIPAddress()) + return base::ASCIIToUTF16(kFallbackIconTextForIP); + domain = url.host(); + } + if (domain.empty()) + return base::string16(); + // TODO(huangs): Handle non-ASCII ("xn--") domain names. + return base::i18n::ToUpper(base::ASCIIToUTF16(domain.substr(0, 1))); }
diff --git a/chrome/browser/favicon/chrome_fallback_icon_client_unittest.cc b/chrome/browser/favicon/chrome_fallback_icon_client_unittest.cc new file mode 100644 index 0000000..81a6b894 --- /dev/null +++ b/chrome/browser/favicon/chrome_fallback_icon_client_unittest.cc
@@ -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. + +#include "chrome/browser/favicon/chrome_fallback_icon_client.h" + +#include "base/macros.h" +#include "base/strings/utf_string_conversions.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "url/gurl.h" + +TEST(ChromeFallbackIconClientTest, GetFontNameList) { + ChromeFallbackIconClient client; + // Just ensure non-empty, otherwise not checking the actual font. + EXPECT_FALSE(client.GetFontNameList().empty()); +} + +TEST(ChromeFallbackIconClientTest, GetFallbackIconText) { + struct { + const char* url_str; + const char* expected; + } test_cases[] = { + // Test vacuous or invalid cases. + {"", ""}, + {"http:///", ""}, + {"this is not an URL", ""}, + {"!@#$%^&*()", ""}, + // Test URLs with a domain in the registry. + {"http://www.google.com/", "G"}, + {"ftp://GOogLE.com/", "G"}, + {"https://www.google.com:8080/path?query#ref", "G"}, + {"http://www.amazon.com", "A"}, + {"http://zmzaon.co.uk/", "Z"}, + {"http://w-3.137.org", "1"}, + // Test URLs with a domian not in the registry. + {"http://localhost/", "L"}, + {"chrome-search://local-ntp/local-ntp.html", "L"}, + // Test IP URLs. + {"http://192.168.0.1/", "IP"}, + {"http://[2001:4860:4860::8888]/", "IP"}, + // Miscellaneous edge cases. + {"http://www..com/", "."}, + {"http://ip.ip/", "I"}, + // xn-- related cases: we're not supporint xn-- yet + {"http://xn--oogle-60a/", "X"}, + {"http://xn-oogle-60a/", "X"}, + }; + for (size_t i = 0; i < arraysize(test_cases); ++i) { + ChromeFallbackIconClient client; + base::string16 expected = base::ASCIIToUTF16(test_cases[i].expected); + GURL url(test_cases[i].url_str); + EXPECT_EQ(expected, client.GetFallbackIconText(url)) + << " for test_cases[" << i << "]"; + } +}
diff --git a/chrome/browser/favicon/large_icon_service_factory.cc b/chrome/browser/favicon/large_icon_service_factory.cc new file mode 100644 index 0000000..56890cb --- /dev/null +++ b/chrome/browser/favicon/large_icon_service_factory.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 "chrome/browser/favicon/large_icon_service_factory.h" + +#include "base/memory/singleton.h" +#include "chrome/browser/favicon/favicon_service_factory.h" +#include "chrome/browser/profiles/profile.h" +#include "components/favicon/core/favicon_service.h" +#include "components/favicon/core/large_icon_service.h" +#include "components/keyed_service/content/browser_context_dependency_manager.h" +#include "content/public/browser/browser_context.h" + +// static +favicon::LargeIconService* LargeIconServiceFactory::GetForBrowserContext( + content::BrowserContext* context) { + return static_cast<favicon::LargeIconService*>( + GetInstance()->GetServiceForBrowserContext(context, true)); +} + +// static +LargeIconServiceFactory* LargeIconServiceFactory::GetInstance() { + return Singleton<LargeIconServiceFactory>::get(); +} + +LargeIconServiceFactory::LargeIconServiceFactory() + : BrowserContextKeyedServiceFactory( + "LargeIconService", + BrowserContextDependencyManager::GetInstance()) { + DependsOn(FaviconServiceFactory::GetInstance()); +} + +LargeIconServiceFactory::~LargeIconServiceFactory() {} + +KeyedService* LargeIconServiceFactory::BuildServiceInstanceFor( + content::BrowserContext* context) const { + favicon::FaviconService* favicon_service = + FaviconServiceFactory::GetForProfile(Profile::FromBrowserContext(context), + ServiceAccessType::EXPLICIT_ACCESS); + return new favicon::LargeIconService(favicon_service); +} + +bool LargeIconServiceFactory::ServiceIsNULLWhileTesting() const { + return true; +}
diff --git a/chrome/browser/favicon/large_icon_service_factory.h b/chrome/browser/favicon/large_icon_service_factory.h new file mode 100644 index 0000000..46a9e5bb --- /dev/null +++ b/chrome/browser/favicon/large_icon_service_factory.h
@@ -0,0 +1,43 @@ +// 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 CHROME_BROWSER_FAVICON_LARGE_ICON_SERVICE_FACTORY_H_ +#define CHROME_BROWSER_FAVICON_LARGE_ICON_SERVICE_FACTORY_H_ + +#include "components/keyed_service/content/browser_context_keyed_service_factory.h" + +template <typename T> struct DefaultSingletonTraits; + +namespace content { +class BrowserContext; +} + +namespace favicon { +class LargeIconService; +} + +// Singleton that owns all LargeIconService and associates them with +// BrowserContext instances. +class LargeIconServiceFactory : public BrowserContextKeyedServiceFactory { + public: + static favicon::LargeIconService* GetForBrowserContext( + content::BrowserContext* context); + + static LargeIconServiceFactory* GetInstance(); + + private: + friend struct DefaultSingletonTraits<LargeIconServiceFactory>; + + LargeIconServiceFactory(); + ~LargeIconServiceFactory() override; + + // BrowserContextKeyedServiceFactory: + KeyedService* BuildServiceInstanceFor( + content::BrowserContext* context) const override; + bool ServiceIsNULLWhileTesting() const override; + + DISALLOW_COPY_AND_ASSIGN(LargeIconServiceFactory); +}; + +#endif // CHROME_BROWSER_FAVICON_LARGE_ICON_SERVICE_FACTORY_H_
diff --git a/chrome/browser/history/top_sites_impl.cc b/chrome/browser/history/top_sites_impl.cc index 92032e9..e904643e 100644 --- a/chrome/browser/history/top_sites_impl.cc +++ b/chrome/browser/history/top_sites_impl.cc
@@ -32,7 +32,6 @@ #include "components/history/core/browser/top_sites_cache.h" #include "components/history/core/browser/url_utils.h" #include "components/history/core/common/thumbnail_score.h" -#include "content/public/browser/browser_thread.h" #include "content/public/browser/navigation_controller.h" #include "content/public/browser/navigation_details.h" #include "content/public/browser/navigation_entry.h" @@ -44,7 +43,6 @@ #include "ui/gfx/image/image_util.h" using base::DictionaryValue; -using content::BrowserThread; using content::NavigationController; namespace history { @@ -136,7 +134,7 @@ bool TopSitesImpl::SetPageThumbnail(const GURL& url, const gfx::Image& thumbnail, const ThumbnailScore& score) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK(thread_checker_.CalledOnValidThread()); if (!loaded_) { // TODO(sky): I need to cache these and apply them after the load @@ -175,7 +173,7 @@ const GURL& url, const base::RefCountedMemory* memory, const ThumbnailScore& score) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK(thread_checker_.CalledOnValidThread()); if (!loaded_) { // TODO(sky): I need to cache these and apply them after the load @@ -310,7 +308,7 @@ } void TopSitesImpl::SyncWithHistory() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK(thread_checker_.CalledOnValidThread()); if (loaded_ && temp_images_.size()) { // If we have temporary thumbnails it means there isn't much data, and most // likely the user is first running Chrome. During this time we throttle @@ -329,7 +327,7 @@ } void TopSitesImpl::AddBlacklistedURL(const GURL& url) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK(thread_checker_.CalledOnValidThread()); base::Value* dummy = base::Value::CreateNullValue(); { @@ -344,7 +342,7 @@ } void TopSitesImpl::RemoveBlacklistedURL(const GURL& url) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK(thread_checker_.CalledOnValidThread()); { DictionaryPrefUpdate update(profile_->GetPrefs(), prefs::kNtpMostVisitedURLsBlacklist); @@ -356,14 +354,14 @@ } bool TopSitesImpl::IsBlacklisted(const GURL& url) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK(thread_checker_.CalledOnValidThread()); const base::DictionaryValue* blacklist = profile_->GetPrefs()->GetDictionary(prefs::kNtpMostVisitedURLsBlacklist); return blacklist && blacklist->HasKey(GetURLHash(url)); } void TopSitesImpl::ClearBlacklistedURLs() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK(thread_checker_.CalledOnValidThread()); { DictionaryPrefUpdate update(profile_->GetPrefs(), prefs::kNtpMostVisitedURLsBlacklist); @@ -600,7 +598,7 @@ } bool TopSitesImpl::AddForcedURL(const GURL& url, const base::Time& time) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK(thread_checker_.CalledOnValidThread()); size_t num_forced = cache_->GetNumForcedURLs(); MostVisitedURLList new_list(cache_->top_sites()); MostVisitedURL new_url; @@ -646,7 +644,7 @@ } size_t TopSitesImpl::MergeCachedForcedURLs(MostVisitedURLList* new_list) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK(thread_checker_.CalledOnValidThread()); // Add all the new URLs for quick lookup. Take that opportunity to count the // number of forced URLs in |new_list|. std::set<GURL> all_new_urls; @@ -757,7 +755,7 @@ void TopSitesImpl::SetTopSites(const MostVisitedURLList& new_top_sites, const CallLocation location) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK(thread_checker_.CalledOnValidThread()); MostVisitedURLList top_sites(new_top_sites); size_t num_forced_urls = MergeCachedForcedURLs(&top_sites); @@ -826,7 +824,7 @@ } int TopSitesImpl::num_results_to_request_from_history() const { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK(thread_checker_.CalledOnValidThread()); const base::DictionaryValue* blacklist = profile_->GetPrefs()->GetDictionary(prefs::kNtpMostVisitedURLsBlacklist); @@ -834,7 +832,7 @@ } void TopSitesImpl::MoveStateToLoaded() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK(thread_checker_.CalledOnValidThread()); MostVisitedURLList filtered_urls_all; MostVisitedURLList filtered_urls_nonforced; @@ -896,7 +894,7 @@ void TopSitesImpl::OnGotMostVisitedThumbnails( const scoped_refptr<MostVisitedThumbnails>& thumbnails) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK(thread_checker_.CalledOnValidThread()); // Set the top sites directly in the cache so that SetTopSites diffs // correctly.
diff --git a/chrome/browser/history/top_sites_impl.h b/chrome/browser/history/top_sites_impl.h index cef762a6..eb9a6ca8 100644 --- a/chrome/browser/history/top_sites_impl.h +++ b/chrome/browser/history/top_sites_impl.h
@@ -18,6 +18,7 @@ #include "base/scoped_observer.h" #include "base/synchronization/lock.h" #include "base/task/cancelable_task_tracker.h" +#include "base/threading/thread_checker.h" #include "base/time/time.h" #include "base/timer/timer.h" #include "components/history/core/browser/history_service.h" @@ -244,6 +245,10 @@ const std::set<GURL>& favicon_urls) override; void HistoryServiceBeingDeleted(HistoryService* history_service) override; + + // Ensures that non thread-safe methods are called on the correct thread. + base::ThreadChecker thread_checker_; + scoped_refptr<TopSitesBackend> backend_; // The top sites data.
diff --git a/chrome/browser/image_decoder.cc b/chrome/browser/image_decoder.cc index b0b9ce8..f8f736a 100644 --- a/chrome/browser/image_decoder.cc +++ b/chrome/browser/image_decoder.cc
@@ -5,6 +5,7 @@ #include "chrome/browser/image_decoder.h" #include "base/bind.h" +#include "base/thread_task_runner_handle.h" #include "chrome/browser/browser_process.h" #include "chrome/common/chrome_utility_messages.h" #include "chrome/grit/generated_resources.h" @@ -36,12 +37,19 @@ ImageDecoder::~ImageDecoder() { } +ImageDecoder::ImageRequest::ImageRequest() + : task_runner_(base::ThreadTaskRunnerHandle::Get()) { + DCHECK(sequence_checker_.CalledOnValidSequencedThread()); +} + ImageDecoder::ImageRequest::ImageRequest( const scoped_refptr<base::SequencedTaskRunner>& task_runner) : task_runner_(task_runner) { + DCHECK(sequence_checker_.CalledOnValidSequencedThread()); } ImageDecoder::ImageRequest::~ImageRequest() { + DCHECK(sequence_checker_.CalledOnValidSequencedThread()); ImageDecoder::Cancel(this); } @@ -56,13 +64,29 @@ const std::string& image_data, ImageCodec image_codec, bool shrink_to_fit) { + g_decoder.Pointer()->StartWithOptionsImpl(image_request, image_data, + image_codec, shrink_to_fit); +} + +void ImageDecoder::StartWithOptionsImpl(ImageRequest* image_request, + const std::string& image_data, + ImageCodec image_codec, + bool shrink_to_fit) { DCHECK(image_request); DCHECK(image_request->task_runner()); + + int request_id; + { + base::AutoLock lock(map_lock_); + request_id = image_request_id_counter_++; + image_request_id_map_.insert(std::make_pair(request_id, image_request)); + } + BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, base::Bind( &ImageDecoder::DecodeImageInSandbox, - g_decoder.Pointer(), image_request, + g_decoder.Pointer(), request_id, std::vector<unsigned char>(image_data.begin(), image_data.end()), image_codec, shrink_to_fit)); } @@ -74,11 +98,17 @@ } void ImageDecoder::DecodeImageInSandbox( - ImageRequest* image_request, + int request_id, const std::vector<unsigned char>& image_data, ImageCodec image_codec, bool shrink_to_fit) { DCHECK_CURRENTLY_ON(BrowserThread::IO); + base::AutoLock lock(map_lock_); + const auto it = image_request_id_map_.find(request_id); + if (it == image_request_id_map_.end()) + return; + + ImageRequest* image_request = it->second; if (!utility_process_host_) { StartBatchMode(); } @@ -88,28 +118,23 @@ // investigation is needed to determine why the utility process // is failing to start. See crbug.com/472272 image_request->task_runner()->PostTask( - FROM_HERE, base::Bind(&ImageRequest::OnDecodeImageFailed, - base::Unretained(image_request))); + FROM_HERE, + base::Bind(&ImageDecoder::RunOnDecodeImageFailed, this, request_id)); return; } last_request_ = base::TimeTicks::Now(); - base::AutoLock lock(map_lock_); - image_request_id_map_.insert( - std::make_pair(image_request_id_counter_, image_request)); switch (image_codec) { case ROBUST_JPEG_CODEC: utility_process_host_->Send(new ChromeUtilityMsg_RobustJPEGDecodeImage( - image_data, image_request_id_counter_)); + image_data, request_id)); break; case DEFAULT_CODEC: utility_process_host_->Send(new ChromeUtilityMsg_DecodeImage( - image_data, shrink_to_fit, image_request_id_counter_)); + image_data, shrink_to_fit, request_id)); break; } - - ++image_request_id_counter_; } void ImageDecoder::CancelImpl(ImageRequest* image_request) { @@ -173,26 +198,58 @@ DCHECK_CURRENTLY_ON(BrowserThread::IO); base::AutoLock lock(map_lock_); auto it = image_request_id_map_.find(request_id); - if (it != image_request_id_map_.end()) { - ImageRequest* image_request = it->second; - image_request->task_runner()->PostTask( - FROM_HERE, base::Bind(&ImageRequest::OnImageDecoded, - base::Unretained(image_request), decoded_image)); + if (it == image_request_id_map_.end()) + return; - image_request_id_map_.erase(it); - } + ImageRequest* image_request = it->second; + image_request->task_runner()->PostTask( + FROM_HERE, + base::Bind(&ImageDecoder::RunOnImageDecoded, + this, + decoded_image, + request_id)); } void ImageDecoder::OnDecodeImageFailed(int request_id) { DCHECK_CURRENTLY_ON(BrowserThread::IO); base::AutoLock lock(map_lock_); auto it = image_request_id_map_.find(request_id); - if (it != image_request_id_map_.end()) { - ImageRequest* image_request = it->second; - image_request->task_runner()->PostTask( - FROM_HERE, base::Bind(&ImageRequest::OnDecodeImageFailed, - base::Unretained(image_request))); + if (it == image_request_id_map_.end()) + return; + ImageRequest* image_request = it->second; + image_request->task_runner()->PostTask( + FROM_HERE, + base::Bind(&ImageDecoder::RunOnDecodeImageFailed, this, request_id)); +} + +void ImageDecoder::RunOnImageDecoded(const SkBitmap& decoded_image, + int request_id) { + ImageRequest* image_request; + { + base::AutoLock lock(map_lock_); + auto it = image_request_id_map_.find(request_id); + if (it == image_request_id_map_.end()) + return; + image_request = it->second; image_request_id_map_.erase(it); } + + DCHECK(image_request->task_runner()->RunsTasksOnCurrentThread()); + image_request->OnImageDecoded(decoded_image); +} + +void ImageDecoder::RunOnDecodeImageFailed(int request_id) { + ImageRequest* image_request; + { + base::AutoLock lock(map_lock_); + auto it = image_request_id_map_.find(request_id); + if (it == image_request_id_map_.end()) + return; + image_request = it->second; + image_request_id_map_.erase(it); + } + + DCHECK(image_request->task_runner()->RunsTasksOnCurrentThread()); + image_request->OnDecodeImageFailed(); }
diff --git a/chrome/browser/image_decoder.h b/chrome/browser/image_decoder.h index 438622c4..ca76043 100644 --- a/chrome/browser/image_decoder.h +++ b/chrome/browser/image_decoder.h
@@ -9,11 +9,11 @@ #include <string> #include <vector> -#include "base/compiler_specific.h" #include "base/lazy_instance.h" #include "base/memory/ref_counted.h" +#include "base/sequence_checker.h" +#include "base/sequenced_task_runner.h" #include "base/synchronization/lock.h" -#include "base/threading/sequenced_worker_pool.h" #include "base/timer/timer.h" #include "content/public/browser/utility_process_host.h" #include "content/public/browser/utility_process_host_client.h" @@ -32,6 +32,8 @@ // to protect the data that is accessed from multiple threads. class ImageDecoder : public content::UtilityProcessHostClient { public: + // ImageRequest objects needs to be created and destroyed on the same + // SequencedTaskRunner. class ImageRequest { public: // Called when image is decoded. @@ -46,14 +48,20 @@ } protected: + // Creates an ImageRequest that runs on the thread creating it. + ImageRequest(); + // Explicitly pass in |task_runner| if the current thread is part of a + // thread pool. explicit ImageRequest( const scoped_refptr<base::SequencedTaskRunner>& task_runner); virtual ~ImageRequest(); private: - // The thread to post OnImageDecoded or OnDecodeImageFailed once the + // The thread to post OnImageDecoded() or OnDecodeImageFailed() once the // the image has been decoded. const scoped_refptr<base::SequencedTaskRunner> task_runner_; + + base::SequenceChecker sequence_checker_; }; enum ImageCodec { @@ -61,7 +69,7 @@ ROBUST_JPEG_CODEC, // Restrict decoding to robust jpeg codec. }; - // Calls StartWithOptions with ImageCodec::DEFAULT_CODEC and + // Calls StartWithOptions() with ImageCodec::DEFAULT_CODEC and // shrink_to_fit = false. static void Start(ImageRequest* image_request, const std::string& image_data); @@ -73,13 +81,15 @@ ImageCodec image_codec, bool shrink_to_fit); - // Removes all instances of image_request from |image_request_id_map_|, + // Removes all instances of |image_request| from |image_request_id_map_|, // ensuring callbacks are not made to the image_request after it is destroyed. static void Cancel(ImageRequest* image_request); private: friend struct base::DefaultLazyInstanceTraits<ImageDecoder>; + using RequestMap = std::map<int, ImageRequest*>; + ImageDecoder(); // It's a reference counted object, so destructor is private. ~ImageDecoder() override; @@ -87,22 +97,24 @@ // Sends a request to the sandboxed process to decode the image. Starts // batch mode if necessary. If the utility process fails to start, // an OnDecodeImageFailed task is posted to image_request's |task_runner_|. - void DecodeImageInSandbox(ImageRequest* image_request, + void DecodeImageInSandbox(int request_id, const std::vector<unsigned char>& image_data, ImageCodec image_codec, bool shrink_to_fit); + void StartWithOptionsImpl(ImageRequest* image_request, + const std::string& image_data, + ImageCodec image_codec, + bool shrink_to_fit); void CancelImpl(ImageRequest* image_request); - using RequestMap = std::map<int, ImageRequest*>; - // Starts UtilityProcessHost in batch mode and starts |batch_mode_timer_|. // If the utility process fails to start, the method resets // |utility_process_host_| and returns. void StartBatchMode(); // Stops batch mode if no requests have come in since - // kBatchModeTimeoutSeconds. + // |kBatchModeTimeoutSeconds|. void StopBatchMode(); // Overidden from UtilityProcessHostClient. @@ -112,7 +124,12 @@ void OnDecodeImageSucceeded(const SkBitmap& decoded_image, int request_id); void OnDecodeImageFailed(int request_id); - // id to use for the next Start request that comes in. + // For the ImageRequest identified by |request_id|, call its OnImageDecoded() + // or OnDecodeImageFailed() method on its task runner thread. + void RunOnImageDecoded(const SkBitmap& decoded_image, int request_id); + void RunOnDecodeImageFailed(int request_id); + + // id to use for the next Start() request that comes in. int image_request_id_counter_; // Map of request id's to ImageRequests. @@ -124,10 +141,10 @@ // The UtilityProcessHost requests are sent to. base::WeakPtr<content::UtilityProcessHost> utility_process_host_; - // Calls StopBatchMode after kBatchModeTimeoutSeconds have elapsed. + // Calls StopBatchMode() after |kBatchModeTimeoutSeconds| have elapsed. base::RepeatingTimer<ImageDecoder> batch_mode_timer_; - // The time Start was last called. + // The time Start() was last called. base::TimeTicks last_request_; DISALLOW_COPY_AND_ASSIGN(ImageDecoder);
diff --git a/chrome/browser/image_decoder_browsertest.cc b/chrome/browser/image_decoder_browsertest.cc new file mode 100644 index 0000000..5621815 --- /dev/null +++ b/chrome/browser/image_decoder_browsertest.cc
@@ -0,0 +1,70 @@ +// 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 "chrome/browser/image_decoder.h" + +#include "chrome/test/base/in_process_browser_test.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/test/test_utils.h" + +using content::BrowserThread; + +namespace { + +class TestImageRequest : public ImageDecoder::ImageRequest { + public: + explicit TestImageRequest(const base::Closure& quit_closure) + : quit_closure_(quit_closure), + quit_called_(false) { + } + + ~TestImageRequest() override { + if (!quit_called_) { + quit_closure_.Run(); + } + } + + private: + void OnImageDecoded(const SkBitmap& decoded_image) override { + Quit(); + } + + void OnDecodeImageFailed() override { + Quit(); + } + + void Quit() { + EXPECT_FALSE(quit_called_); + quit_called_ = true; + quit_closure_.Run(); + } + + base::Closure quit_closure_; + bool quit_called_; + + DISALLOW_COPY_AND_ASSIGN(TestImageRequest); +}; + +} // namespace + +class ImageDecoderBrowserTest : public InProcessBrowserTest { +}; + +IN_PROC_BROWSER_TEST_F(ImageDecoderBrowserTest, Basic) { + scoped_refptr<content::MessageLoopRunner> runner = + new content::MessageLoopRunner; + TestImageRequest test_request(runner->QuitClosure()); + ImageDecoder::Start(&test_request, std::string()); + runner->Run(); +} + +IN_PROC_BROWSER_TEST_F(ImageDecoderBrowserTest, StartAndDestroy) { + scoped_refptr<content::MessageLoopRunner> runner = + new content::MessageLoopRunner; + scoped_ptr<TestImageRequest> test_request( + new TestImageRequest(runner->QuitClosure())); + ImageDecoder::Start(test_request.get(), std::string()); + test_request.reset(); + runner->Run(); +}
diff --git a/chrome/browser/image_holder_unittest.cc b/chrome/browser/image_holder_unittest.cc index 948f11e5..03da2ca3 100644 --- a/chrome/browser/image_holder_unittest.cc +++ b/chrome/browser/image_holder_unittest.cc
@@ -5,6 +5,7 @@ #include <string> #include "chrome/browser/image_holder.h" +#include "content/public/test/test_browser_thread_bundle.h" #include "testing/gtest/include/gtest/gtest.h" namespace { @@ -15,11 +16,20 @@ class TestDelegate : public chrome::ImageHolderDelegate { public: TestDelegate() : on_fetch_complete_called_(false) {} + + bool on_fetch_complete_called() const { return on_fetch_complete_called_; } + + // chrome::ImageHolderDelegate void OnFetchComplete() override { on_fetch_complete_called_ = true; } + + private: + content::TestBrowserThreadBundle thread_bundle_; bool on_fetch_complete_called_; + + DISALLOW_COPY_AND_ASSIGN(TestDelegate); }; -} // namespace. +} // namespace namespace chrome { @@ -27,22 +37,23 @@ TEST_F(ImageHolderTest, CreateBitmapFetcherTest) { TestDelegate delegate; - ImageHolder image_holder(GURL(kIconUrl1), GURL(kIconUrl2), NULL, &delegate); + ImageHolder image_holder(GURL(kIconUrl1), GURL(kIconUrl2), nullptr, + &delegate); + ASSERT_EQ(2U, image_holder.fetchers_.size()); EXPECT_EQ(GURL(kIconUrl1), image_holder.fetchers_[0]->url()); EXPECT_EQ(GURL(kIconUrl2), image_holder.fetchers_[1]->url()); - EXPECT_EQ(static_cast<unsigned int>(2), image_holder.fetchers_.size()); // Adding a dup of an existing URL shouldn't change anything. image_holder.CreateBitmapFetcher(GURL(kIconUrl2)); + ASSERT_EQ(2U, image_holder.fetchers_.size()); EXPECT_EQ(GURL(kIconUrl1), image_holder.fetchers_[0]->url()); EXPECT_EQ(GURL(kIconUrl2), image_holder.fetchers_[1]->url()); - EXPECT_EQ(static_cast<unsigned int>(2), image_holder.fetchers_.size()); } TEST_F(ImageHolderTest, OnFetchCompleteTest) { TestDelegate delegate; - ImageHolder image_holder(GURL(kIconUrl1), GURL(), NULL, &delegate); + ImageHolder image_holder(GURL(kIconUrl1), GURL(), nullptr, &delegate); // Put a real bitmap into "bitmap". 2x2 bitmap of green 32 bit pixels. SkBitmap bitmap; @@ -55,15 +66,16 @@ EXPECT_FALSE(image_holder.low_dpi_image().IsEmpty()); // Expect that we reported the fetch done to the delegate. - EXPECT_TRUE(delegate.on_fetch_complete_called_); + EXPECT_TRUE(delegate.on_fetch_complete_called()); } TEST_F(ImageHolderTest, IsFetchingDoneTest) { TestDelegate delegate; - ImageHolder image_holder1(GURL(kIconUrl1), GURL(kIconUrl2), NULL, &delegate); - ImageHolder image_holder2(GURL(kIconUrl1), GURL(), NULL, &delegate); - ImageHolder image_holder3(GURL(), GURL(kIconUrl2), NULL, &delegate); - ImageHolder image_holder4(GURL(), GURL(), NULL, &delegate); + ImageHolder image_holder1(GURL(kIconUrl1), GURL(kIconUrl2), nullptr, + &delegate); + ImageHolder image_holder2(GURL(kIconUrl1), GURL(), nullptr, &delegate); + ImageHolder image_holder3(GURL(), GURL(kIconUrl2), nullptr, &delegate); + ImageHolder image_holder4(GURL(), GURL(), nullptr, &delegate); // Initially, image holder 4 with no URLs should report done, but no others. EXPECT_FALSE(image_holder1.IsFetchingDone());
diff --git a/chrome/browser/io_thread.cc b/chrome/browser/io_thread.cc index 097e8a6..34fe6b88 100644 --- a/chrome/browser/io_thread.cc +++ b/chrome/browser/io_thread.cc
@@ -781,10 +781,30 @@ new net::ChannelIDService( new net::DefaultChannelIDStore(NULL), base::WorkerPool::GetTaskRunner(true))); + // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/466432 + // is fixed. + tracked_objects::ScopedTracker tracking_profile12_1( + FROM_HERE_WITH_EXPLICIT_FUNCTION( + "466432 IOThread::InitAsync::CreateDnsProbeService")); globals_->dns_probe_service.reset(new chrome_browser_net::DnsProbeService()); + // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/466432 + // is fixed. + tracked_objects::ScopedTracker tracking_profile12_2( + FROM_HERE_WITH_EXPLICIT_FUNCTION( + "466432 IOThread::InitAsync::CreateHostMappingRules")); globals_->host_mapping_rules.reset(new net::HostMappingRules()); + // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/466432 + // is fixed. + tracked_objects::ScopedTracker tracking_profile12_3( + FROM_HERE_WITH_EXPLICIT_FUNCTION( + "466432 IOThread::InitAsync::CreateHTTPUserAgentSettings")); globals_->http_user_agent_settings.reset( new net::StaticHttpUserAgentSettings(std::string(), GetUserAgent())); + // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/466432 + // is fixed. + tracked_objects::ScopedTracker tracking_profile12_4( + FROM_HERE_WITH_EXPLICIT_FUNCTION( + "466432 IOThread::InitAsync::CommandLineConfiguration")); if (command_line.HasSwitch(switches::kHostRules)) { TRACE_EVENT_BEGIN0("startup", "IOThread::InitAsync:SetRulesFromString"); globals_->host_mapping_rules->SetRulesFromString( @@ -803,6 +823,11 @@ globals_->testing_fixed_https_port = GetSwitchValueAsInt(command_line, switches::kTestingFixedHttpsPort); } + // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/466432 + // is fixed. + tracked_objects::ScopedTracker tracking_profile12_5( + FROM_HERE_WITH_EXPLICIT_FUNCTION( + "466432 IOThread::InitAsync::QuicConfiguration")); ConfigureQuic(command_line); if (command_line.HasSwitch( switches::kEnableUserAlternateProtocolPorts)) {
diff --git a/chrome/browser/media/chrome_webrtc_audio_quality_browsertest.cc b/chrome/browser/media/chrome_webrtc_audio_quality_browsertest.cc index f7079817..41b733e 100644 --- a/chrome/browser/media/chrome_webrtc_audio_quality_browsertest.cc +++ b/chrome/browser/media/chrome_webrtc_audio_quality_browsertest.cc
@@ -480,6 +480,14 @@ return wav_filename; } +void DeleteFileUnlessTestFailed(const base::FilePath& path, bool recursive) { + if (::testing::Test::HasFailure()) + printf("Test failed; keeping recording(s) at\n\t%" PRFilePath ".\n", + path.value().c_str()); + else + EXPECT_TRUE(base::DeleteFile(path, recursive)); +} + std::vector<base::FilePath> ListWavFilesInDir(const base::FilePath& dir) { base::FileEnumerator files(dir, false, base::FileEnumerator::FILES, FILE_PATH_LITERAL("*.wav")); @@ -503,7 +511,7 @@ ASSERT_TRUE(SplitFileOnSilence( trimmed_audio, workdir.Append(FILE_PATH_LITERAL("output.wav")))); - ASSERT_TRUE(base::DeleteFile(trimmed_audio, false)); + DeleteFileUnlessTestFailed(trimmed_audio, false); } // Computes the difference between the actual and reference segment. A positive @@ -575,16 +583,18 @@ std::string raw_mos; std::string mos_lqo; - ASSERT_TRUE(RunPesq(trimmed_reference, trimmed_recording, 16000, - &raw_mos, &mos_lqo)); + bool succeeded = RunPesq(trimmed_reference, trimmed_recording, 16000, + &raw_mos, &mos_lqo); + EXPECT_TRUE(succeeded) << "Failed to run PESQ."; + if (succeeded) { + perf_test::PrintResult( + "audio_pesq", perf_modifier, "raw_mos", raw_mos, "score", true); + perf_test::PrintResult( + "audio_pesq", perf_modifier, "mos_lqo", mos_lqo, "score", true); + } - perf_test::PrintResult( - "audio_pesq", perf_modifier, "raw_mos", raw_mos, "score", true); - perf_test::PrintResult( - "audio_pesq", perf_modifier, "mos_lqo", mos_lqo, "score", true); - - EXPECT_TRUE(base::DeleteFile(trimmed_reference, false)); - EXPECT_TRUE(base::DeleteFile(trimmed_recording, false)); + DeleteFileUnlessTestFailed(trimmed_reference, false); + DeleteFileUnlessTestFailed(trimmed_recording, false); } } // namespace @@ -649,9 +659,9 @@ ASSERT_NO_FATAL_FAILURE(SetupAndRecordAudioCall( reference_file, recording, kAudioOnlyCallConstraints, base::TimeDelta::FromSeconds(25))); - ComputeAndPrintPesqResults(reference_file, recording, "_getusermedia"); - EXPECT_TRUE(base::DeleteFile(recording, false)); + ComputeAndPrintPesqResults(reference_file, recording, "_getusermedia"); + DeleteFileUnlessTestFailed(recording, false); } IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcAudioQualityBrowserTest, @@ -744,8 +754,6 @@ reference_file, recording, constraints, base::TimeDelta::FromSeconds(25))); - // Call Take() on the scoped temp dirs if you want to look at the files after - // the test exits (the default is to delete the files). base::ScopedTempDir split_ref_files; ASSERT_TRUE(split_ref_files.CreateUniqueTempDir()); ASSERT_NO_FATAL_FAILURE( @@ -757,13 +765,17 @@ ASSERT_TRUE(split_actual_files.CreateUniqueTempDir()); ASSERT_NO_FATAL_FAILURE( SplitFileOnSilenceIntoDir(recording, split_actual_files.path())); + + // Keep the recording and split files if the analysis fails. + base::FilePath actual_files_dir = split_actual_files.Take(); std::vector<base::FilePath> actual_segments = - ListWavFilesInDir(split_actual_files.path()); + ListWavFilesInDir(actual_files_dir); - AnalyzeSegmentsAndPrintResult(ref_segments, actual_segments, reference_file, - perf_modifier); + AnalyzeSegmentsAndPrintResult( + ref_segments, actual_segments, reference_file, perf_modifier); - EXPECT_TRUE(base::DeleteFile(recording, false)); + DeleteFileUnlessTestFailed(recording, false); + DeleteFileUnlessTestFailed(actual_files_dir, true); } // The AGC should apply non-zero gain here.
diff --git a/chrome/browser/media_galleries/fileapi/supported_image_type_validator.cc b/chrome/browser/media_galleries/fileapi/supported_image_type_validator.cc index ac590cd..2e83a76 100644 --- a/chrome/browser/media_galleries/fileapi/supported_image_type_validator.cc +++ b/chrome/browser/media_galleries/fileapi/supported_image_type_validator.cc
@@ -52,9 +52,7 @@ ImageDecoderDelegateAdapter( scoped_ptr<std::string> data, const storage::CopyOrMoveFileValidator::ResultCallback& callback) - : ImageRequest(content::BrowserThread::GetMessageLoopProxyForThread( - BrowserThread::IO)), - data_(data.Pass()), + : data_(data.Pass()), callback_(callback) { DCHECK(data_); }
diff --git a/chrome/browser/net/certificate_error_reporter.cc b/chrome/browser/net/certificate_error_reporter.cc index 8d5b5d8..279209c 100644 --- a/chrome/browser/net/certificate_error_reporter.cc +++ b/chrome/browser/net/certificate_error_reporter.cc
@@ -48,7 +48,7 @@ break; case REPORT_TYPE_EXTENDED_REPORTING: // TODO(estark): Encrypt the report if not sending over HTTPS. - DCHECK(upload_url_.SchemeUsesTLS()); + DCHECK(upload_url_.SchemeIsCryptographic()); SendCertLoggerRequest(request); break; default:
diff --git a/chrome/browser/net/chrome_net_log.cc b/chrome/browser/net/chrome_net_log.cc index 1dda8cce..674ee64 100644 --- a/chrome/browser/net/chrome_net_log.cc +++ b/chrome/browser/net/chrome_net_log.cc
@@ -19,14 +19,52 @@ #include "net/log/trace_net_log_observer.h" #include "net/log/write_to_file_net_log_observer.h" +namespace { + +net::NetLogCaptureMode GetCaptureModeFromCommandLine( + const base::CommandLine& command_line) { + const net::NetLogCaptureMode kDefault = net::NetLogCaptureMode::Default(); + + // TODO(eroman): The NetLog "capture mode" used to be called the "log + // level". To preserve backwards compatibility the log level of old is + // converted into a capture mode. + if (!command_line.HasSwitch(switches::kNetLogLevel)) + return kDefault; + + std::string log_level_string = + command_line.GetSwitchValueASCII(switches::kNetLogLevel); + + int level; + if (!base::StringToInt(log_level_string, &level)) + return kDefault; + + switch (level) { + case 0: + return net::NetLogCaptureMode::IncludeSocketBytes(); + case 1: + return net::NetLogCaptureMode::IncludeCookiesAndCredentials(); + case 2: + return net::NetLogCaptureMode::Default(); + case 3: + return net::NetLogCaptureMode::None(); + default: + LOG(ERROR) << "Unrecognized --" << switches::kNetLogLevel; + break; + } + + return kDefault; +} + +} // namespace + ChromeNetLog::ChromeNetLog() : net_log_temp_file_(new NetLogTempFile(this)) { - const base::CommandLine* command_line = - base::CommandLine::ForCurrentProcess(); + const base::CommandLine& command_line = + *base::CommandLine::ForCurrentProcess(); - if (command_line->HasSwitch(switches::kLogNetLog)) { + if (command_line.HasSwitch(switches::kLogNetLog)) { base::FilePath log_path = - command_line->GetSwitchValuePath(switches::kLogNetLog); + command_line.GetSwitchValuePath(switches::kLogNetLog); // Much like logging.h, bypass threading restrictions by using fopen // directly. Have to write on a thread that's shutdown to handle events on // shutdown properly, and posting events to another thread as they occur @@ -46,17 +84,11 @@ } else { scoped_ptr<base::Value> constants(NetInternalsUI::GetConstants()); net_log_logger_.reset(new net::WriteToFileNetLogObserver()); - if (command_line->HasSwitch(switches::kNetLogLevel)) { - std::string log_level_string = - command_line->GetSwitchValueASCII(switches::kNetLogLevel); - int command_line_log_level; - if (base::StringToInt(log_level_string, &command_line_log_level) && - command_line_log_level >= LOG_ALL && - command_line_log_level <= LOG_NONE) { - net_log_logger_->set_log_level( - static_cast<LogLevel>(command_line_log_level)); - } - } + + net::NetLogCaptureMode capture_mode = + GetCaptureModeFromCommandLine(command_line); + net_log_logger_->set_capture_mode(capture_mode); + net_log_logger_->StartObserving(this, file.Pass(), constants.get(), nullptr); }
diff --git a/chrome/browser/net/net_log_temp_file.cc b/chrome/browser/net/net_log_temp_file.cc index 3674b2b..99f29aa 100644 --- a/chrome/browser/net/net_log_temp_file.cc +++ b/chrome/browser/net/net_log_temp_file.cc
@@ -93,19 +93,20 @@ return dict; } -net::NetLog::LogLevel NetLogTempFile::GetLogLevelForLogType(LogType log_type) { +net::NetLogCaptureMode NetLogTempFile::GetCaptureModeForLogType( + LogType log_type) { switch (log_type) { case LOG_TYPE_LOG_BYTES: - return net::NetLog::LOG_ALL; + return net::NetLogCaptureMode::IncludeSocketBytes(); case LOG_TYPE_NORMAL: - return net::NetLog::LOG_ALL_BUT_BYTES; + return net::NetLogCaptureMode::IncludeCookiesAndCredentials(); case LOG_TYPE_STRIP_PRIVATE_DATA: - return net::NetLog::LOG_STRIP_PRIVATE_DATA; + return net::NetLogCaptureMode::Default(); case LOG_TYPE_NONE: case LOG_TYPE_UNKNOWN: NOTREACHED(); } - return net::NetLog::LOG_STRIP_PRIVATE_DATA; + return net::NetLogCaptureMode::Default(); } bool NetLogTempFile::EnsureInit() { @@ -145,7 +146,7 @@ scoped_ptr<base::Value> constants(NetInternalsUI::GetConstants()); net_log_logger_.reset(new net::WriteToFileNetLogObserver()); - net_log_logger_->set_log_level(GetLogLevelForLogType(log_type)); + net_log_logger_->set_capture_mode(GetCaptureModeForLogType(log_type)); net_log_logger_->StartObserving(chrome_net_log_, file.Pass(), constants.get(), nullptr); }
diff --git a/chrome/browser/net/net_log_temp_file.h b/chrome/browser/net/net_log_temp_file.h index 46598e3..2b96276 100644 --- a/chrome/browser/net/net_log_temp_file.h +++ b/chrome/browser/net/net_log_temp_file.h
@@ -113,8 +113,8 @@ LOG_TYPE_STRIP_PRIVATE_DATA, }; - // Returns the NetLog::LogLevel corresponding to a LogType. - static net::NetLog::LogLevel GetLogLevelForLogType(LogType log_type); + // Returns the NetLog::CaptureMode corresponding to a LogType. + static net::NetLogCaptureMode GetCaptureModeForLogType(LogType log_type); // Initializes the |state_| to STATE_NOT_LOGGING and |log_type_| to // LOG_TYPE_NONE (if there is no temporary file from earlier run) or @@ -123,8 +123,9 @@ bool EnsureInit(); // Start collecting NetLog data into chrome-net-export-log.json file in - // base::GetTempDir() directory, using the specified log level. It is a no-op - // if we are already collecting data into a file, and |log_level| is ignored. + // base::GetTempDir() directory, using the specified capture mode. It is a + // no-op if we are already collecting data into a file, and |capture_mode| is + // ignored. // TODO(mmenke): That's rather weird behavior, think about improving it. void StartNetLog(LogType log_type);
diff --git a/chrome/browser/net/net_log_temp_file_unittest.cc b/chrome/browser/net/net_log_temp_file_unittest.cc index 89b0b62..572f12c 100644 --- a/chrome/browser/net/net_log_temp_file_unittest.cc +++ b/chrome/browser/net/net_log_temp_file_unittest.cc
@@ -148,19 +148,21 @@ // initialized by a DO_START command of the given type. void VerifyFileAndStateAfterDoStart() { - VerifyFileAndStateAfterStart(NetLogTempFile::LOG_TYPE_NORMAL, "NORMAL", - net::NetLog::LOG_ALL_BUT_BYTES); + VerifyFileAndStateAfterStart( + NetLogTempFile::LOG_TYPE_NORMAL, "NORMAL", + net::NetLogCaptureMode::IncludeCookiesAndCredentials()); } void VerifyFileAndStateAfterDoStartStripPrivateData() const { VerifyFileAndStateAfterStart(NetLogTempFile::LOG_TYPE_STRIP_PRIVATE_DATA, "STRIP_PRIVATE_DATA", - net::NetLog::LOG_STRIP_PRIVATE_DATA); + net::NetLogCaptureMode::Default()); } void VerifyFileAndStateAfterDoStartLogBytes() const { VerifyFileAndStateAfterStart(NetLogTempFile::LOG_TYPE_LOG_BYTES, - "LOG_BYTES", net::NetLog::LOG_ALL); + "LOG_BYTES", + net::NetLogCaptureMode::IncludeSocketBytes()); } // Make sure the export file has been successfully initialized after DO_STOP @@ -191,13 +193,13 @@ void VerifyFileAndStateAfterStart( NetLogTempFile::LogType expected_log_type, const std::string& expected_log_type_string, - net::NetLog::LogLevel expected_log_level) const { + net::NetLogCaptureMode expected_capture_mode) const { EXPECT_EQ(NetLogTempFile::STATE_LOGGING, net_log_temp_file_->state()); EXPECT_EQ("LOGGING", GetStateString()); EXPECT_EQ(expected_log_type, net_log_temp_file_->log_type()); EXPECT_EQ(expected_log_type_string, GetLogTypeString()); - EXPECT_EQ(expected_log_level, net_log_->GetLogLevel()); + EXPECT_EQ(expected_capture_mode, net_log_->GetCaptureMode()); // Check GetFilePath returns false when still writing to the file. base::FilePath net_export_file_path;
diff --git a/chrome/browser/net/predictor_browsertest.cc b/chrome/browser/net/predictor_browsertest.cc index 48b9d66..c0ff9590 100644 --- a/chrome/browser/net/predictor_browsertest.cc +++ b/chrome/browser/net/predictor_browsertest.cc
@@ -121,7 +121,7 @@ void Attach() { g_browser_process->net_log()->DeprecatedAddObserver( - this, net::NetLog::LOG_ALL_BUT_BYTES); + this, net::NetLogCaptureMode::IncludeCookiesAndCredentials()); } void Detach() {
diff --git a/chrome/browser/net/spdyproxy/data_reduction_proxy_chrome_io_data.cc b/chrome/browser/net/spdyproxy/data_reduction_proxy_chrome_io_data.cc index 66cbc15..319969e9 100644 --- a/chrome/browser/net/spdyproxy/data_reduction_proxy_chrome_io_data.cc +++ b/chrome/browser/net/spdyproxy/data_reduction_proxy_chrome_io_data.cc
@@ -5,6 +5,7 @@ #include "chrome/browser/net/spdyproxy/data_reduction_proxy_chrome_io_data.h" #include "chrome/browser/net/spdyproxy/data_reduction_proxy_chrome_settings.h" +#include "chrome/common/chrome_content_client.h" #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.h" #include "components/data_reduction_proxy/core/common/data_reduction_proxy_params.h" @@ -52,7 +53,7 @@ data_reduction_proxy_io_data( new data_reduction_proxy::DataReductionProxyIOData( DataReductionProxyChromeSettings::GetClient(), flags, net_log, - io_task_runner, ui_task_runner, enable_quic)); + io_task_runner, ui_task_runner, enable_quic, GetUserAgent())); data_reduction_proxy_io_data->InitOnUIThread(prefs); #if defined(ENABLE_DATA_REDUCTION_PROXY_DEBUGGING)
diff --git a/chrome/browser/password_manager/password_store_factory.cc b/chrome/browser/password_manager/password_store_factory.cc index 78b24257..7acd80d 100644 --- a/chrome/browser/password_manager/password_store_factory.cc +++ b/chrome/browser/password_manager/password_store_factory.cc
@@ -103,6 +103,12 @@ !profile_sync_service->IsUsingSecondaryPassphrase(); } +bool ShouldPropagatingPasswordChangesToWebCredentialsBeEnabled() { + base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); + return password_manager::IsPropagatingPasswordChangesToWebCredentialsEnabled( + *command_line); +} + void ActivateAffiliationBasedMatching(PasswordStore* password_store, Profile* profile) { DCHECK(password_store); @@ -124,6 +130,8 @@ affiliation_service.Pass())); affiliated_match_helper->Initialize(); password_store->SetAffiliatedMatchHelper(affiliated_match_helper.Pass()); + password_store->enable_propagating_password_changes_to_web_credentials( + ShouldPropagatingPasswordChangesToWebCredentialsBeEnabled()); } } // namespace
diff --git a/chrome/browser/plugins/plugin_info_message_filter.cc b/chrome/browser/plugins/plugin_info_message_filter.cc index 29cd4c3..03c30a5 100644 --- a/chrome/browser/plugins/plugin_info_message_filter.cc +++ b/chrome/browser/plugins/plugin_info_message_filter.cc
@@ -364,6 +364,7 @@ plugin_metadata->identifier(), &plugin_setting, &uses_default_content_setting, &is_managed); + bool legacy_ask_user = plugin_setting == CONTENT_SETTING_ASK; plugin_setting = content_settings::PluginsFieldTrial::EffectiveContentSetting( CONTENT_SETTINGS_TYPE_PLUGINS, plugin_setting); @@ -431,7 +432,9 @@ if (plugin_setting == CONTENT_SETTING_DETECT_IMPORTANT_CONTENT) { *status = ChromeViewHostMsg_GetPluginInfo_Status::kPlayImportantContent; } else if (plugin_setting == CONTENT_SETTING_BLOCK) { - *status = is_managed + // For managed users with the ASK policy, we allow manually running plugins + // via context menu. This is the closest to admin intent. + *status = is_managed && !legacy_ask_user ? ChromeViewHostMsg_GetPluginInfo_Status::kBlockedByPolicy : ChromeViewHostMsg_GetPluginInfo_Status::kBlocked; }
diff --git a/chrome/browser/prerender/prerender_browsertest.cc b/chrome/browser/prerender/prerender_browsertest.cc index 05c0b3a..db6e9f6 100644 --- a/chrome/browser/prerender/prerender_browsertest.cc +++ b/chrome/browser/prerender/prerender_browsertest.cc
@@ -434,8 +434,7 @@ const content::Referrer& referrer, Origin origin, FinalStatus expected_final_status) - : PrerenderContents(prerender_manager, profile, url, - referrer, origin, PrerenderManager::kNoExperiment), + : PrerenderContents(prerender_manager, profile, url, referrer, origin), expected_final_status_(expected_final_status), new_render_view_host_(NULL), was_hidden_(false), @@ -666,8 +665,7 @@ Profile* profile, const GURL& url, const content::Referrer& referrer, - Origin origin, - uint8 experiment_id) override { + Origin origin) override { ExpectedContents expected; if (!expected_contents_queue_.empty()) { expected = expected_contents_queue_.front();
diff --git a/chrome/browser/prerender/prerender_contents.cc b/chrome/browser/prerender/prerender_contents.cc index 44c67f2..fbf9d0a 100644 --- a/chrome/browser/prerender/prerender_contents.cc +++ b/chrome/browser/prerender/prerender_contents.cc
@@ -70,10 +70,9 @@ Profile* profile, const GURL& url, const content::Referrer& referrer, - Origin origin, - uint8 experiment_id) override { - return new PrerenderContents(prerender_manager, profile, - url, referrer, origin, experiment_id); + Origin origin) override { + return new PrerenderContents(prerender_manager, profile, url, referrer, + origin); } }; @@ -189,8 +188,7 @@ Profile* profile, const GURL& url, const content::Referrer& referrer, - Origin origin, - uint8 experiment_id) + Origin origin) : prerendering_has_started_(false), session_storage_namespace_id_(-1), prerender_manager_(prerender_manager), @@ -206,14 +204,13 @@ child_id_(-1), route_id_(-1), origin_(origin), - experiment_id_(experiment_id), network_bytes_(0) { DCHECK(prerender_manager != NULL); } PrerenderContents* PrerenderContents::CreateMatchCompleteReplacement() { PrerenderContents* new_contents = prerender_manager_->CreatePrerenderContents( - prerender_url(), referrer(), origin(), experiment_id()); + prerender_url(), referrer(), origin()); new_contents->load_start_time_ = load_start_time_; new_contents->session_storage_namespace_id_ = session_storage_namespace_id_; @@ -274,7 +271,7 @@ // Everything after this point sets up the WebContents object and associated // RenderView for the prerender page. Don't do this for members of the // control group. - if (prerender_manager_->IsControlGroup(experiment_id())) + if (prerender_manager_->IsControlGroup()) return; prerendering_has_started_ = true; @@ -363,7 +360,7 @@ DCHECK_NE(ORIGIN_MAX, origin()); prerender_manager_->RecordFinalStatusWithMatchCompleteStatus( - origin(), experiment_id(), match_complete_status(), final_status()); + origin(), match_complete_status(), final_status()); bool used = final_status() == FINAL_STATUS_USED || final_status() == FINAL_STATUS_WOULD_HAVE_BEEN_USED; @@ -633,7 +630,7 @@ // not reach the PrerenderHandle. Rather // OnPrerenderCreatedMatchCompleteReplacement will propogate that // information to the referer. - if (!prerender_manager_->IsControlGroup(experiment_id()) && + if (!prerender_manager_->IsControlGroup() && (prerendering_has_started() || match_complete_status() == MATCH_COMPLETE_REPLACEMENT)) { NotifyPrerenderStop();
diff --git a/chrome/browser/prerender/prerender_contents.h b/chrome/browser/prerender/prerender_contents.h index b31f7e7..138e01d 100644 --- a/chrome/browser/prerender/prerender_contents.h +++ b/chrome/browser/prerender/prerender_contents.h
@@ -62,8 +62,7 @@ Profile* profile, const GURL& url, const content::Referrer& referrer, - Origin origin, - uint8 experiment_id) = 0; + Origin origin) = 0; private: DISALLOW_COPY_AND_ASSIGN(Factory); @@ -183,7 +182,6 @@ FinalStatus final_status() const { return final_status_; } Origin origin() const { return origin_; } - uint8 experiment_id() const { return experiment_id_; } base::TimeTicks load_start_time() const { return load_start_time_; } @@ -273,8 +271,7 @@ Profile* profile, const GURL& url, const content::Referrer& referrer, - Origin origin, - uint8 experiment_id); + Origin origin); // Set the final status for how the PrerenderContents was used. This // should only be called once, and should be called before the prerender @@ -391,9 +388,6 @@ // Origin for this prerender. Origin origin_; - // Experiment during which this prerender is performed. - uint8 experiment_id_; - // The size of the WebView from the launching page. gfx::Size size_;
diff --git a/chrome/browser/prerender/prerender_histograms.cc b/chrome/browser/prerender/prerender_histograms.cc index f70e847..46a35ff 100644 --- a/chrome/browser/prerender/prerender_histograms.cc +++ b/chrome/browser/prerender/prerender_histograms.cc
@@ -32,21 +32,11 @@ return std::string("Prerender.") + prefix_type + std::string("_") + name; } -std::string GetHistogramName(Origin origin, uint8 experiment_id, - bool is_wash, const std::string& name) { +std::string GetHistogramName(Origin origin, bool is_wash, + const std::string& name) { if (is_wash) return ComposeHistogramName("wash", name); - if (origin == ORIGIN_GWS_PRERENDER) { - if (experiment_id == kNoExperiment) - return ComposeHistogramName("gws", name); - return ComposeHistogramName("exp" + std::string(1, experiment_id + '0'), - name); - } - - if (experiment_id != kNoExperiment) - return ComposeHistogramName("wash", name); - switch (origin) { case ORIGIN_OMNIBOX: return ComposeHistogramName("omnibox", name); @@ -62,7 +52,8 @@ return ComposeHistogramName("Instant", name); case ORIGIN_LINK_REL_NEXT: return ComposeHistogramName("webnext", name); - case ORIGIN_GWS_PRERENDER: // Handled above. + case ORIGIN_GWS_PRERENDER: + return ComposeHistogramName("gws", name); default: NOTREACHED(); break; @@ -79,44 +70,27 @@ } // namespace -// Helper macros for experiment-based and origin-based histogram reporting. -// All HISTOGRAM arguments must be UMA_HISTOGRAM... macros that contain an -// argument "name" which these macros will eventually substitute for the -// actual name used. +// Helper macros for origin-based histogram reporting. All HISTOGRAM arguments +// must be UMA_HISTOGRAM... macros that contain an argument "name" which these +// macros will eventually substitute for the actual name used. #define PREFIXED_HISTOGRAM(histogram_name, origin, HISTOGRAM) \ - PREFIXED_HISTOGRAM_INTERNAL(origin, GetCurrentExperimentId(), \ - IsOriginExperimentWash(), HISTOGRAM, \ - histogram_name) + PREFIXED_HISTOGRAM_INTERNAL(origin, IsOriginWash(), HISTOGRAM, histogram_name) #define PREFIXED_HISTOGRAM_ORIGIN_EXPERIMENT(histogram_name, origin, \ - experiment, HISTOGRAM) \ - PREFIXED_HISTOGRAM_INTERNAL(origin, experiment, false, HISTOGRAM, \ - histogram_name) + HISTOGRAM) \ + PREFIXED_HISTOGRAM_INTERNAL(origin, false, HISTOGRAM, histogram_name) -#define PREFIXED_HISTOGRAM_INTERNAL(origin, experiment, wash, HISTOGRAM, \ - histogram_name) do { \ +#define PREFIXED_HISTOGRAM_INTERNAL(origin, wash, HISTOGRAM, histogram_name) \ +do { \ { \ /* Do not rename. HISTOGRAM expects a local variable "name". */ \ std::string name = ComposeHistogramName(std::string(), histogram_name); \ HISTOGRAM; \ } \ /* Do not rename. HISTOGRAM expects a local variable "name". */ \ - std::string name = GetHistogramName(origin, experiment, wash, \ - histogram_name); \ - /* Usually, a browsing session should only have a single experiment. */ \ - /* Therefore, when there is a second experiment ID other than the one */ \ - /* being recorded, don't record anything. */ \ - /* Furthermore, experiments only apply if the origin is GWS. Should there */ \ - /* somehow be an experiment ID if the origin is not GWS, ignore the */ \ - /* experiment ID. */ \ - static uint8 recording_experiment = kNoExperiment; \ - if (recording_experiment == kNoExperiment && experiment != kNoExperiment) \ - recording_experiment = experiment; \ + std::string name = GetHistogramName(origin, wash, histogram_name); \ if (wash) { \ HISTOGRAM; \ - } else if (experiment != kNoExperiment && \ - (origin != ORIGIN_GWS_PRERENDER || \ - experiment != recording_experiment)) { \ } else if (origin == ORIGIN_OMNIBOX) { \ HISTOGRAM; \ } else if (origin == ORIGIN_NONE) { \ @@ -131,41 +105,33 @@ HISTOGRAM; \ } else if (origin == ORIGIN_LINK_REL_NEXT) { \ HISTOGRAM; \ - } else if (experiment != kNoExperiment) { \ - HISTOGRAM; \ } else { \ HISTOGRAM; \ } \ } while (0) PrerenderHistograms::PrerenderHistograms() - : last_experiment_id_(kNoExperiment), - last_origin_(ORIGIN_MAX), - origin_experiment_wash_(false), + : last_origin_(ORIGIN_MAX), + origin_wash_(false), seen_any_pageload_(true), seen_pageload_started_after_prerender_(true) { } void PrerenderHistograms::RecordPrerender(Origin origin, const GURL& url) { - // Check if we are doing an experiment. - uint8 experiment = GetQueryStringBasedExperiment(url); - - // We need to update last_experiment_id_, last_origin_, and - // origin_experiment_wash_. + // We need to update last_origin_ and origin_wash_. if (!WithinWindow()) { // If we are outside a window, this is a fresh start and we are fine, // and there is no mix. - origin_experiment_wash_ = false; + origin_wash_ = false; } else { - // If we are inside the last window, there is a mish mash of origins - // and experiments if either there was a mish mash before, or the current - // experiment/origin does not match the previous one. - if (experiment != last_experiment_id_ || origin != last_origin_) - origin_experiment_wash_ = true; + // If we are inside the last window, there is a mish mash of origins if + // either there was a mish mash before, or the current origin does not match + // the previous one. + if (origin != last_origin_) + origin_wash_ = true; } last_origin_ = origin; - last_experiment_id_ = experiment; // If we observe multiple tags within the 30 second window, we will still // reset the window to begin at the most recent occurrence, so that we will @@ -374,7 +340,6 @@ void PrerenderHistograms::RecordFinalStatus( Origin origin, - uint8 experiment_id, PrerenderContents::MatchCompleteStatus mc_status, FinalStatus final_status) const { DCHECK(final_status != FINAL_STATUS_MAX); @@ -382,14 +347,14 @@ if (mc_status == PrerenderContents::MATCH_COMPLETE_DEFAULT || mc_status == PrerenderContents::MATCH_COMPLETE_REPLACED) { PREFIXED_HISTOGRAM_ORIGIN_EXPERIMENT( - "FinalStatus", origin, experiment_id, + "FinalStatus", origin, UMA_HISTOGRAM_ENUMERATION(name, final_status, FINAL_STATUS_MAX)); } if (mc_status == PrerenderContents::MATCH_COMPLETE_DEFAULT || mc_status == PrerenderContents::MATCH_COMPLETE_REPLACEMENT || mc_status == PrerenderContents::MATCH_COMPLETE_REPLACEMENT_PENDING) { PREFIXED_HISTOGRAM_ORIGIN_EXPERIMENT( - "FinalStatusMatchComplete", origin, experiment_id, + "FinalStatusMatchComplete", origin, UMA_HISTOGRAM_ENUMERATION(name, final_status, FINAL_STATUS_MAX)); } } @@ -426,16 +391,10 @@ } } -uint8 PrerenderHistograms::GetCurrentExperimentId() const { - if (!WithinWindow()) - return kNoExperiment; - return last_experiment_id_; -} - -bool PrerenderHistograms::IsOriginExperimentWash() const { +bool PrerenderHistograms::IsOriginWash() const { if (!WithinWindow()) return false; - return origin_experiment_wash_; + return origin_wash_; } } // namespace prerender
diff --git a/chrome/browser/prerender/prerender_histograms.h b/chrome/browser/prerender/prerender_histograms.h index c161e0a..0f7bc40 100644 --- a/chrome/browser/prerender/prerender_histograms.h +++ b/chrome/browser/prerender/prerender_histograms.h
@@ -77,7 +77,6 @@ // Record a final status of a prerendered page in a histogram. void RecordFinalStatus(Origin origin, - uint8 experiment_id, PrerenderContents::MatchCompleteStatus mc_status, FinalStatus final_status) const; @@ -117,23 +116,15 @@ // observed. bool WithinWindow() const; - // Returns the current experiment. - uint8 GetCurrentExperimentId() const; - - // Returns whether or not there is currently an origin/experiment wash. - bool IsOriginExperimentWash() const; - - // An integer indicating a Prerendering Experiment being currently conducted. - // (The last experiment ID seen). - uint8 last_experiment_id_; + // Returns whether or not there is currently an origin wash. + bool IsOriginWash() const; // Origin of the last prerender seen. Origin last_origin_; // A boolean indicating that we have recently encountered a combination of - // different experiments and origins, making an attribution of PPLT's to - // experiments / origins impossible. - bool origin_experiment_wash_; + // different origins, making an attribution of PPLT's to origins impossible. + bool origin_wash_; // The time when we last saw a prerender request coming from a renderer. // This is used to record perceived PLT's for a certain amount of time
diff --git a/chrome/browser/prerender/prerender_manager.cc b/chrome/browser/prerender/prerender_manager.cc index 1a77ef9..0c144f4 100644 --- a/chrome/browser/prerender/prerender_manager.cc +++ b/chrome/browser/prerender/prerender_manager.cc
@@ -367,9 +367,6 @@ return NULL; } - if (IsNoSwapInExperiment(prerender_data->contents()->experiment_id())) - return NULL; - if (WebContents* new_web_contents = prerender_data->contents()->prerender_contents()) { if (web_contents == new_web_contents) @@ -624,13 +621,12 @@ // static bool PrerenderManager::ActuallyPrerendering() { - return IsPrerenderingPossible() && !IsControlGroup(kNoExperiment); + return IsPrerenderingPossible() && !IsControlGroup(); } // static -bool PrerenderManager::IsControlGroup(uint8 experiment_id) { - return GetMode() == PRERENDER_MODE_EXPERIMENT_CONTROL_GROUP || - IsControlGroupExperiment(experiment_id); +bool PrerenderManager::IsControlGroup() { + return GetMode() == PRERENDER_MODE_EXPERIMENT_CONTROL_GROUP; } // static @@ -774,7 +770,7 @@ dict_value->SetBoolean("omnibox_enabled", IsOmniboxEnabled(profile_)); // If prerender is disabled via a flag this method is not even called. std::string enabled_note; - if (IsControlGroup(kNoExperiment)) + if (IsControlGroup()) enabled_note += "(Control group: Not actually prerendering) "; if (IsNoUseGroup()) enabled_note += "(No-use group: Not swapping in prerendered pages) "; @@ -797,13 +793,9 @@ void PrerenderManager::RecordFinalStatusWithMatchCompleteStatus( Origin origin, - uint8 experiment_id, PrerenderContents::MatchCompleteStatus mc_status, FinalStatus final_status) const { - histograms_->RecordFinalStatus(origin, - experiment_id, - mc_status, - final_status); + histograms_->RecordFinalStatus(origin, mc_status, final_status); } void PrerenderManager::RecordNavigation(const GURL& url) { @@ -916,11 +908,8 @@ GURL url = url_arg; GURL alias_url; - uint8 experiment = GetQueryStringBasedExperiment(url_arg); - if (IsControlGroup(experiment) && - MaybeGetQueryStringBasedAliasURL(url, &alias_url)) { + if (IsControlGroup() && MaybeGetQueryStringBasedAliasURL(url, &alias_url)) url = alias_url; - } // From here on, we will record a FinalStatus so we need to register with the // histogram tracking. @@ -929,7 +918,7 @@ if (PrerenderData* preexisting_prerender_data = FindPrerenderData(url, session_storage_namespace)) { RecordFinalStatusWithoutCreatingPrerenderContents( - url, origin, experiment, FINAL_STATUS_DUPLICATE); + url, origin, FINAL_STATUS_DUPLICATE); return new PrerenderHandle(preexisting_prerender_data); } @@ -948,7 +937,7 @@ profile_, url) && !content::RenderProcessHost::run_renderer_in_process()) { RecordFinalStatusWithoutCreatingPrerenderContents( - url, origin, experiment, FINAL_STATUS_TOO_MANY_PROCESSES); + url, origin, FINAL_STATUS_TOO_MANY_PROCESSES); return NULL; } @@ -958,12 +947,12 @@ // this doesn't make sense as the next prerender request will be triggered // by a navigation and is unlikely to be the same site. RecordFinalStatusWithoutCreatingPrerenderContents( - url, origin, experiment, FINAL_STATUS_RATE_LIMIT_EXCEEDED); + url, origin, FINAL_STATUS_RATE_LIMIT_EXCEEDED); return NULL; } - PrerenderContents* prerender_contents = CreatePrerenderContents( - url, referrer, origin, experiment); + PrerenderContents* prerender_contents = CreatePrerenderContents(url, referrer, + origin); DCHECK(prerender_contents); active_prerenders_.push_back( new PrerenderData(this, prerender_contents, @@ -989,8 +978,7 @@ prerender_contents->StartPrerendering(contents_size, session_storage_namespace); - DCHECK(IsControlGroup(experiment) || - prerender_contents->prerendering_has_started()); + DCHECK(IsControlGroup() || prerender_contents->prerendering_has_started()); if (GetMode() == PRERENDER_MODE_EXPERIMENT_MULTI_PRERENDER_GROUP) histograms_->RecordConcurrency(active_prerenders_.size()); @@ -1092,11 +1080,10 @@ PrerenderContents* PrerenderManager::CreatePrerenderContents( const GURL& url, const content::Referrer& referrer, - Origin origin, - uint8 experiment_id) { + Origin origin) { DCHECK(CalledOnValidThread()); return prerender_contents_factory_->CreatePrerenderContents( - this, profile_, url, referrer, origin, experiment_id); + this, profile_, url, referrer, origin); } void PrerenderManager::SortActivePrerenders() { @@ -1209,21 +1196,17 @@ prerender_contents->set_match_complete_status( PrerenderContents::MATCH_COMPLETE_REPLACED); histograms_->RecordFinalStatus(prerender_contents->origin(), - prerender_contents->experiment_id(), PrerenderContents::MATCH_COMPLETE_REPLACEMENT, FINAL_STATUS_WOULD_HAVE_BEEN_USED); prerender_contents->Destroy(final_status); } void PrerenderManager::RecordFinalStatusWithoutCreatingPrerenderContents( - const GURL& url, Origin origin, uint8 experiment_id, - FinalStatus final_status) const { + const GURL& url, Origin origin, FinalStatus final_status) const { PrerenderHistory::Entry entry(url, final_status, origin, base::Time::Now()); prerender_history_->AddEntry(entry); RecordFinalStatusWithMatchCompleteStatus( - origin, experiment_id, - PrerenderContents::MATCH_COMPLETE_DEFAULT, - final_status); + origin, PrerenderContents::MATCH_COMPLETE_DEFAULT, final_status); } void PrerenderManager::Observe(int type,
diff --git a/chrome/browser/prerender/prerender_manager.h b/chrome/browser/prerender/prerender_manager.h index 00f638b4..2ba76b4 100644 --- a/chrome/browser/prerender/prerender_manager.h +++ b/chrome/browser/prerender/prerender_manager.h
@@ -87,9 +87,6 @@ CLEAR_MAX = 0x1 << 2 }; - // ID indicating that no experiment is active. - static const uint8 kNoExperiment = 0; - // Owned by a Profile object for the lifetime of the profile. explicit PrerenderManager(Profile* profile); @@ -178,7 +175,7 @@ static const char* GetModeString(); static bool IsPrerenderingPossible(); static bool ActuallyPrerendering(); - static bool IsControlGroup(uint8 experiment_id); + static bool IsControlGroup(); static bool IsNoUseGroup(); // Query the list of current prerender pages to see if the given web contents @@ -243,7 +240,6 @@ // (necessary to flag MatchComplete dummies). void RecordFinalStatusWithMatchCompleteStatus( Origin origin, - uint8 experiment_id, PrerenderContents::MatchCompleteStatus mc_status, FinalStatus final_status) const; @@ -419,8 +415,7 @@ virtual PrerenderContents* CreatePrerenderContents( const GURL& url, const content::Referrer& referrer, - Origin origin, - uint8 experiment_id); + Origin origin); // Insures the |active_prerenders_| are sorted by increasing expiry time. Call // after every mutation of active_prerenders_ that can possibly make it @@ -476,8 +471,7 @@ // This is a helper function which will ultimately call // RecordFinalStatusWthMatchCompleteStatus, using MATCH_COMPLETE_DEFAULT. void RecordFinalStatusWithoutCreatingPrerenderContents( - const GURL& url, Origin origin, uint8 experiment_id, - FinalStatus final_status) const; + const GURL& url, Origin origin, FinalStatus final_status) const; // Swaps a prerender |prerender_data| for |url| into the tab, replacing
diff --git a/chrome/browser/prerender/prerender_resource_throttle_unittest.cc b/chrome/browser/prerender/prerender_resource_throttle_unittest.cc index ed8798e..a66296e 100644 --- a/chrome/browser/prerender/prerender_resource_throttle_unittest.cc +++ b/chrome/browser/prerender/prerender_resource_throttle_unittest.cc
@@ -38,8 +38,7 @@ TestPrerenderContents(PrerenderManager* prerender_manager, int child_id, int route_id) : PrerenderContents(prerender_manager, static_cast<Profile*>(NULL), - GURL(), content::Referrer(), ORIGIN_NONE, - PrerenderManager::kNoExperiment), + GURL(), content::Referrer(), ORIGIN_NONE), child_id_(child_id), route_id_(route_id) { PrerenderResourceThrottle::OverridePrerenderContentsForTesting(this);
diff --git a/chrome/browser/prerender/prerender_unittest.cc b/chrome/browser/prerender/prerender_unittest.cc index 9d226155..b0e37e1 100644 --- a/chrome/browser/prerender/prerender_unittest.cc +++ b/chrome/browser/prerender/prerender_unittest.cc
@@ -232,8 +232,7 @@ PrerenderContents* CreatePrerenderContents(const GURL& url, const Referrer& referrer, - Origin origin, - uint8 experiment_id) override { + Origin origin) override { CHECK(next_prerender_contents_.get()); EXPECT_EQ(url, next_prerender_contents_->prerender_url()); EXPECT_EQ(origin, next_prerender_contents_->origin()); @@ -269,8 +268,7 @@ Origin origin, FinalStatus expected_final_status) : PrerenderContents(test_prerender_manager, - NULL, url, Referrer(), origin, - PrerenderManager::kNoExperiment), + NULL, url, Referrer(), origin), route_id_(g_next_route_id_++), test_prerender_manager_(test_prerender_manager), expected_final_status_(expected_final_status) { @@ -289,7 +287,7 @@ // but it will early exit before actually creating a new RenderView if // |is_control_group| is true; load_start_time_ = test_prerender_manager_->GetCurrentTimeTicks(); - if (!test_prerender_manager_->IsControlGroup(experiment_id())) { + if (!test_prerender_manager_->IsControlGroup()) { prerendering_has_started_ = true; test_prerender_manager_->DummyPrerenderContentsStarted(-1, route_id_, this); NotifyPrerenderStart();
diff --git a/chrome/browser/prerender/prerender_util.cc b/chrome/browser/prerender/prerender_util.cc index 26f84ce..f779907 100644 --- a/chrome/browser/prerender/prerender_util.cc +++ b/chrome/browser/prerender/prerender_util.cc
@@ -71,26 +71,6 @@ return false; } -uint8 GetQueryStringBasedExperiment(const GURL& url) { - url::Parsed parsed; - url::ParseStandardURL(url.spec().c_str(), url.spec().length(), &parsed); - url::Component query = parsed.query; - url::Component key, value; - while (url::ExtractQueryKeyValue(url.spec().c_str(), &query, &key, &value)) { - if (key.len != 3 || strncmp(url.spec().c_str() + key.begin, "lpe", key.len)) - continue; - - // We found a lpe= query string component. - if (value.len != 1) - continue; - uint8 exp = *(url.spec().c_str() + value.begin) - '0'; - if (exp < 1 || exp > 9) - continue; - return exp; - } - return kNoExperiment; -} - bool IsGoogleDomain(const GURL& url) { return StartsWithASCII(url.host(), std::string("www.google."), true); } @@ -104,16 +84,6 @@ StartsWithASCII(url.path(), std::string("/webhp"), true)); } -bool IsNoSwapInExperiment(uint8 experiment_id) { - // Currently, experiments 5 and 6 fall in this category. - return experiment_id == 5 || experiment_id == 6; -} - -bool IsControlGroupExperiment(uint8 experiment_id) { - // Currently, experiments 7 and 8 fall in this category. - return experiment_id == 7 || experiment_id == 8; -} - void ReportPrerenderExternalURL() { ReportPrerenderSchemeCancelReason( PRERENDER_SCHEME_CANCEL_REASON_EXTERNAL_PROTOCOL);
diff --git a/chrome/browser/prerender/prerender_util.h b/chrome/browser/prerender/prerender_util.h index 2d7e3c2..3a9ec1f 100644 --- a/chrome/browser/prerender/prerender_util.h +++ b/chrome/browser/prerender/prerender_util.h
@@ -10,36 +10,17 @@ namespace prerender { -// ID indicating that no experiment is active. -const uint8 kNoExperiment = 0; - // Extracts a urlencoded URL stored in a url= query parameter from a URL // supplied, if available, and stores it in alias_url. Returns whether or not // the operation succeeded (i.e. a valid URL was found). bool MaybeGetQueryStringBasedAliasURL(const GURL& url, GURL* alias_url); -// Extracts an experiment stored in the query parameter -// lpe= from the URL supplied, and returns it. -// Returns kNoExperiment if no experiment ID is found, or if the ID -// is not an integer in the range 1 to 9. -uint8 GetQueryStringBasedExperiment(const GURL& url); - // Indicates whether the URL provided has a Google domain bool IsGoogleDomain(const GURL& url); // Indicates whether the URL provided could be a Google search result page. bool IsGoogleSearchResultURL(const GURL& url); -// The prerender contents of some experiments should never be swapped in -// by pretending to never match on the URL. This function will return true -// iff this is the case for the experiment_id specified. -bool IsNoSwapInExperiment(uint8 experiment_id); - -// The prerender contents of some experiments should behave identical to the -// control group, regardless of the field trial. This function will return true -// iff this is the case for the experiment_id specified. -bool IsControlGroupExperiment(uint8 experiment_id); - // Report a URL was canceled due to trying to handle an external URL. void ReportPrerenderExternalURL();
diff --git a/chrome/browser/prerender/prerender_util_unittest.cc b/chrome/browser/prerender/prerender_util_unittest.cc index ebfea81..3e316cbb 100644 --- a/chrome/browser/prerender/prerender_util_unittest.cc +++ b/chrome/browser/prerender/prerender_util_unittest.cc
@@ -35,29 +35,6 @@ ASSERT_EQ(GURL("http://validURLSareGREAT.com").spec(), result.spec()); } -// Ensure that extracting an experiment in the lpe= query string component -// works. -TEST(PrerenderUtilTest, ExtractExperimentInQueryStringTest) { - EXPECT_EQ( - GetQueryStringBasedExperiment(GURL( - "http://www.google.com/url?sa=t&source=web&cd=1&ved=0CBcQFjAA&url=h" - "ttp%3A%2F%2Fwww.abercrombie.com%2Fwebapp%2Fwcs%2Fstores%2Fservlet%" - "2FStoreLocator%3FcatalogId%3D%26storeId%3D10051%26langId%3D-1&rct=" - "j&q=allinurl%3A%26&ei=KLyUTYGSEdTWiAKUmLCdCQ&usg=AFQjCNF8nJ2MpBFfr" - "1ijO39_f22bcKyccw&sig2=2ymyGpO0unJwU1d4kdCUjQ&lpe=4&asdf=test")), - 4); - EXPECT_EQ(GetQueryStringBasedExperiment( - GURL("http://www.google.com/test.php?a=b")), kNoExperiment); - EXPECT_EQ(GetQueryStringBasedExperiment( - GURL("http://www.google.com/test.php?lpe=5")), 5); - EXPECT_EQ(GetQueryStringBasedExperiment( - GURL("http://www.google.com/test.php?lpe=50")), kNoExperiment); - EXPECT_EQ(GetQueryStringBasedExperiment( - GURL("http://www.google.com/test.php?lpe=0")), kNoExperiment); - EXPECT_EQ(GetQueryStringBasedExperiment( - GURL("http://www.google.com/test.php?lpe=10")), kNoExperiment); -} - // Ensure that we detect Google search result URLs correctly. TEST(PrerenderUtilTest, DetectGoogleSearchREsultURLTest) { EXPECT_TRUE(IsGoogleSearchResultURL(GURL("http://www.google.com/#asdf")));
diff --git a/chrome/browser/printing/print_dialog_cloud.cc b/chrome/browser/printing/print_dialog_cloud.cc index 1a729381..5dfc021 100644 --- a/chrome/browser/printing/print_dialog_cloud.cc +++ b/chrome/browser/printing/print_dialog_cloud.cc
@@ -602,8 +602,7 @@ void CloudPrintWebDialogDelegate::OnCloseContents(WebContents* source, bool* out_close_dialog) { - if (out_close_dialog) - *out_close_dialog = true; + *out_close_dialog = true; } bool CloudPrintWebDialogDelegate::ShouldShowDialogTitle() const {
diff --git a/chrome/browser/printing/print_preview_dialog_controller.cc b/chrome/browser/printing/print_preview_dialog_controller.cc index 6f92e20..618c3834 100644 --- a/chrome/browser/printing/print_preview_dialog_controller.cc +++ b/chrome/browser/printing/print_preview_dialog_controller.cc
@@ -139,8 +139,7 @@ void PrintPreviewDialogDelegate::OnCloseContents(WebContents* /* source */, bool* out_close_dialog) { - if (out_close_dialog) - *out_close_dialog = true; + *out_close_dialog = true; } bool PrintPreviewDialogDelegate::ShouldShowDialogTitle() const {
diff --git a/chrome/browser/printing/printer_manager_dialog_linux.cc b/chrome/browser/printing/printer_manager_dialog_linux.cc index 8513fa7a..a568b99 100644 --- a/chrome/browser/printing/printer_manager_dialog_linux.cc +++ b/chrome/browser/printing/printer_manager_dialog_linux.cc
@@ -35,9 +35,9 @@ case base::nix::DESKTOP_ENVIRONMENT_KDE3: case base::nix::DESKTOP_ENVIRONMENT_KDE4: case base::nix::DESKTOP_ENVIRONMENT_UNITY: + case base::nix::DESKTOP_ENVIRONMENT_XFCE: command = kGNOMEPrinterConfigCommand; break; - case base::nix::DESKTOP_ENVIRONMENT_XFCE: case base::nix::DESKTOP_ENVIRONMENT_OTHER: break; }
diff --git a/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc b/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc index 9c33815c..d1b3b97a 100644 --- a/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc +++ b/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
@@ -15,6 +15,8 @@ #include "chrome/browser/dom_distiller/dom_distiller_service_factory.h" #include "chrome/browser/domain_reliability/service_factory.h" #include "chrome/browser/download/download_service_factory.h" +#include "chrome/browser/engagement/site_engagement_service.h" +#include "chrome/browser/engagement/site_engagement_service_factory.h" #include "chrome/browser/favicon/favicon_service_factory.h" #include "chrome/browser/geolocation/geolocation_permission_context_factory.h" #include "chrome/browser/google/google_url_tracker_factory.h" @@ -276,6 +278,10 @@ #endif ShortcutsBackendFactory::GetInstance(); SigninManagerFactory::GetInstance(); + + if (SiteEngagementService::IsEnabled()) + SiteEngagementServiceFactory::GetInstance(); + #if defined(ENABLE_SPELLCHECK) SpellcheckServiceFactory::GetInstance(); #endif
diff --git a/chrome/browser/profiles/profile_downloader.cc b/chrome/browser/profiles/profile_downloader.cc index 2377772..ab7237b 100644 --- a/chrome/browser/profiles/profile_downloader.cc +++ b/chrome/browser/profiles/profile_downloader.cc
@@ -193,9 +193,7 @@ } ProfileDownloader::ProfileDownloader(ProfileDownloaderDelegate* delegate) - : ImageRequest( - BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI)), - OAuth2TokenService::Consumer("profile_downloader"), + : OAuth2TokenService::Consumer("profile_downloader"), delegate_(delegate), picture_status_(PICTURE_FAILED) { DCHECK(delegate_);
diff --git a/chrome/browser/profiles/profile_impl_io_data.cc b/chrome/browser/profiles/profile_impl_io_data.cc index 721cdc5..e82ef90f 100644 --- a/chrome/browser/profiles/profile_impl_io_data.cc +++ b/chrome/browser/profiles/profile_impl_io_data.cc
@@ -132,7 +132,11 @@ if (io_data_->http_server_properties_manager_) io_data_->http_server_properties_manager_->ShutdownOnPrefThread(); - io_data_->data_reduction_proxy_io_data()->ShutdownOnUIThread(); + // io_data_->data_reduction_proxy_io_data() might be NULL if Init() was + // never called. + if (io_data_->data_reduction_proxy_io_data()) + io_data_->data_reduction_proxy_io_data()->ShutdownOnUIThread(); + io_data_->ShutdownOnUIThread(GetAllContextGetters().Pass()); }
diff --git a/chrome/browser/push_messaging/push_messaging_browsertest.cc b/chrome/browser/push_messaging/push_messaging_browsertest.cc index 9a1b15ca..024749f 100644 --- a/chrome/browser/push_messaging/push_messaging_browsertest.cc +++ b/chrome/browser/push_messaging/push_messaging_browsertest.cc
@@ -153,10 +153,10 @@ void SetUpOnMainThread() override { gcm_service_ = static_cast<gcm::FakeGCMProfileService*>( gcm::GCMProfileServiceFactory::GetInstance()->SetTestingFactoryAndUse( - browser()->profile(), &gcm::FakeGCMProfileService::Build)); + GetBrowser()->profile(), &gcm::FakeGCMProfileService::Build)); gcm_service_->set_collect(true); push_service_ = - PushMessagingServiceFactory::GetForProfile(browser()->profile()); + PushMessagingServiceFactory::GetForProfile(GetBrowser()->profile()); LoadTestPage(); @@ -173,7 +173,7 @@ } void LoadTestPage(const std::string& path) { - ui_test_utils::NavigateToURL(browser(), https_server_->GetURL(path)); + ui_test_utils::NavigateToURL(GetBrowser(), https_server_->GetURL(path)); } void LoadTestPage() { @@ -187,14 +187,14 @@ bool RunScript(const std::string& script, std::string* result, content::WebContents* web_contents) { if (!web_contents) - web_contents = browser()->tab_strip_model()->GetActiveWebContents(); + web_contents = GetBrowser()->tab_strip_model()->GetActiveWebContents(); return content::ExecuteScriptAndExtractString(web_contents->GetMainFrame(), script, result); } - void TryToRegisterSuccessfully( - const std::string& expected_push_registration_id); + void TryToSubscribeSuccessfully( + const std::string& expected_push_subscription_id); PushMessagingApplicationId GetServiceWorkerAppId( int64 service_worker_registration_id); @@ -220,6 +220,8 @@ return "files/push_messaging/test.html"; } + virtual Browser* GetBrowser() const { return browser(); } + private: scoped_ptr<net::SpawnedTestServer> https_server_; gcm::FakeGCMProfileService* gcm_service_; @@ -236,44 +238,44 @@ }; IN_PROC_BROWSER_TEST_F(PushMessagingBadManifestBrowserTest, - RegisterFailsNotVisibleMessages) { + SubscribeFailsNotVisibleMessages) { std::string script_result; ASSERT_TRUE(RunScript("registerServiceWorker()", &script_result)); ASSERT_EQ("ok - service worker registered", script_result); - ASSERT_TRUE(RunScript("registerPush()", &script_result)); + ASSERT_TRUE(RunScript("subscribePush()", &script_result)); EXPECT_EQ("AbortError - Registration failed - permission denied", script_result); } -void PushMessagingBrowserTest::TryToRegisterSuccessfully( - const std::string& expected_push_registration_id) { +void PushMessagingBrowserTest::TryToSubscribeSuccessfully( + const std::string& expected_push_subscription_id) { std::string script_result; EXPECT_TRUE(RunScript("registerServiceWorker()", &script_result)); EXPECT_EQ("ok - service worker registered", script_result); - InfoBarResponder accepting_responder(browser(), true); + InfoBarResponder accepting_responder(GetBrowser(), true); EXPECT_TRUE(RunScript("requestNotificationPermission()", &script_result)); EXPECT_EQ("permission status - granted", script_result); - EXPECT_TRUE(RunScript("registerPush()", &script_result)); + EXPECT_TRUE(RunScript("subscribePush()", &script_result)); EXPECT_EQ(std::string(kPushMessagingEndpoint) + " - " - + expected_push_registration_id, script_result); + + expected_push_subscription_id, script_result); } PushMessagingApplicationId PushMessagingBrowserTest::GetServiceWorkerAppId( int64 service_worker_registration_id) { GURL origin = https_server()->GetURL(std::string()).GetOrigin(); PushMessagingApplicationId application_id = PushMessagingApplicationId::Get( - browser()->profile(), origin, service_worker_registration_id); + GetBrowser()->profile(), origin, service_worker_registration_id); EXPECT_TRUE(application_id.IsValid()); return application_id; } IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, - RegisterSuccessNotificationsGranted) { - TryToRegisterSuccessfully("1-0" /* expected_push_registration_id */); + SubscribeSuccessNotificationsGranted) { + TryToSubscribeSuccessfully("1-0" /* expected_push_subscription_id */); PushMessagingApplicationId app_id = GetServiceWorkerAppId(0LL); EXPECT_EQ(app_id.app_id_guid(), gcm_service()->last_registered_app_id()); @@ -281,14 +283,14 @@ } IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, - RegisterSuccessNotificationsPrompt) { + SubscribeSuccessNotificationsPrompt) { std::string script_result; ASSERT_TRUE(RunScript("registerServiceWorker()", &script_result)); ASSERT_EQ("ok - service worker registered", script_result); - InfoBarResponder accepting_responder(browser(), true); - ASSERT_TRUE(RunScript("registerPush()", &script_result)); + InfoBarResponder accepting_responder(GetBrowser(), true); + ASSERT_TRUE(RunScript("subscribePush()", &script_result)); EXPECT_EQ(std::string(kPushMessagingEndpoint) + " - 1-0", script_result); PushMessagingApplicationId app_id = GetServiceWorkerAppId(0LL); @@ -297,50 +299,50 @@ } IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, - RegisterFailureNotificationsBlocked) { + SubscribeFailureNotificationsBlocked) { std::string script_result; ASSERT_TRUE(RunScript("registerServiceWorker()", &script_result)); ASSERT_EQ("ok - service worker registered", script_result); - InfoBarResponder cancelling_responder(browser(), false); + InfoBarResponder cancelling_responder(GetBrowser(), false); ASSERT_TRUE(RunScript("requestNotificationPermission();", &script_result)); ASSERT_EQ("permission status - denied", script_result); - ASSERT_TRUE(RunScript("registerPush()", &script_result)); + ASSERT_TRUE(RunScript("subscribePush()", &script_result)); EXPECT_EQ("AbortError - Registration failed - permission denied", script_result); } -IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, RegisterFailureNoManifest) { +IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, SubscribeFailureNoManifest) { std::string script_result; ASSERT_TRUE(RunScript("registerServiceWorker()", &script_result)); ASSERT_EQ("ok - service worker registered", script_result); - InfoBarResponder accepting_responder(browser(), true); + InfoBarResponder accepting_responder(GetBrowser(), true); ASSERT_TRUE(RunScript("requestNotificationPermission();", &script_result)); ASSERT_EQ("permission status - granted", script_result); ASSERT_TRUE(RunScript("removeManifest()", &script_result)); ASSERT_EQ("manifest removed", script_result); - ASSERT_TRUE(RunScript("registerPush()", &script_result)); + ASSERT_TRUE(RunScript("subscribePush()", &script_result)); EXPECT_EQ("AbortError - Registration failed - no sender id provided", script_result); } -// TODO(johnme): Test registering from a worker - see https://crbug.com/437298. +// TODO(johnme): Test subscribing from a worker - see https://crbug.com/437298. -IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, RegisterPersisted) { +IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, SubscribePersisted) { std::string script_result; // First, test that Service Worker registration IDs are assigned in order of - // registering the Service Workers, and the (fake) push registration ids are - // assigned in order of push registration (even when these orders are + // registering the Service Workers, and the (fake) push subscription ids are + // assigned in order of push subscription (even when these orders are // different). - TryToRegisterSuccessfully("1-0" /* expected_push_registration_id */); + TryToSubscribeSuccessfully("1-0" /* expected_push_subscription_id */); PushMessagingApplicationId app_id_sw0 = GetServiceWorkerAppId(0LL); EXPECT_EQ(app_id_sw0.app_id_guid(), gcm_service()->last_registered_app_id()); @@ -356,16 +358,16 @@ // navigator.serviceWorker.ready is going to be resolved with the parent // Service Worker which still controls the page. LoadTestPage("files/push_messaging/subscope2/test.html"); - TryToRegisterSuccessfully("1-1" /* expected_push_registration_id */); + TryToSubscribeSuccessfully("1-1" /* expected_push_subscription_id */); PushMessagingApplicationId app_id_sw2 = GetServiceWorkerAppId(2LL); EXPECT_EQ(app_id_sw2.app_id_guid(), gcm_service()->last_registered_app_id()); LoadTestPage("files/push_messaging/subscope1/test.html"); - TryToRegisterSuccessfully("1-2" /* expected_push_registration_id */); + TryToSubscribeSuccessfully("1-2" /* expected_push_subscription_id */); PushMessagingApplicationId app_id_sw1 = GetServiceWorkerAppId(1LL); EXPECT_EQ(app_id_sw1.app_id_guid(), gcm_service()->last_registered_app_id()); - // Now test that the Service Worker registration IDs and push registration IDs + // Now test that the Service Worker registration IDs and push subscription IDs // generated above were persisted to SW storage, by checking that they are // unchanged despite requesting them in a different order. // TODO(johnme): Ideally we would restart the browser at this point to check @@ -374,22 +376,22 @@ // so we wouldn't be able to load the test pages with the same origin. LoadTestPage("files/push_messaging/subscope1/test.html"); - TryToRegisterSuccessfully("1-2" /* expected_push_registration_id */); + TryToSubscribeSuccessfully("1-2" /* expected_push_subscription_id */); EXPECT_EQ(app_id_sw1.app_id_guid(), gcm_service()->last_registered_app_id()); LoadTestPage("files/push_messaging/subscope2/test.html"); - TryToRegisterSuccessfully("1-1" /* expected_push_registration_id */); + TryToSubscribeSuccessfully("1-1" /* expected_push_subscription_id */); EXPECT_EQ(app_id_sw1.app_id_guid(), gcm_service()->last_registered_app_id()); LoadTestPage(); - TryToRegisterSuccessfully("1-0" /* expected_push_registration_id */); + TryToSubscribeSuccessfully("1-0" /* expected_push_subscription_id */); EXPECT_EQ(app_id_sw1.app_id_guid(), gcm_service()->last_registered_app_id()); } IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, PushEventSuccess) { std::string script_result; - TryToRegisterSuccessfully("1-0" /* expected_push_registration_id */); + TryToSubscribeSuccessfully("1-0" /* expected_push_subscription_id */); PushMessagingApplicationId app_id = GetServiceWorkerAppId(0LL); EXPECT_EQ(app_id.app_id_guid(), gcm_service()->last_registered_app_id()); @@ -414,7 +416,7 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, PushEventNoServiceWorker) { std::string script_result; - TryToRegisterSuccessfully("1-0" /* expected_push_registration_id */); + TryToSubscribeSuccessfully("1-0" /* expected_push_subscription_id */); PushMessagingApplicationId app_id = GetServiceWorkerAppId(0LL); EXPECT_EQ(app_id.app_id_guid(), gcm_service()->last_registered_app_id()); @@ -456,7 +458,7 @@ PushEventEnforcesUserVisibleNotification) { std::string script_result; - TryToRegisterSuccessfully("1-0" /* expected_push_registration_id */); + TryToSubscribeSuccessfully("1-0" /* expected_push_subscription_id */); PushMessagingApplicationId app_id = GetServiceWorkerAppId(0LL); EXPECT_EQ(app_id.app_id_guid(), gcm_service()->last_registered_app_id()); @@ -476,7 +478,7 @@ // We'll need to specify the web_contents in which to eval script, since we're // going to run script in a background tab. content::WebContents* web_contents = - browser()->tab_strip_model()->GetActiveWebContents(); + GetBrowser()->tab_strip_model()->GetActiveWebContents(); // If the site is visible in an active tab, we should not force a notification // to be shown. Try it twice, since we allow one mistake per 10 push events. @@ -492,7 +494,7 @@ // Open a blank foreground tab so site is no longer visible. ui_test_utils::NavigateToURLWithDisposition( - browser(), GURL("about:blank"), NEW_FOREGROUND_TAB, + GetBrowser(), GURL("about:blank"), NEW_FOREGROUND_TAB, ui_test_utils::BROWSER_TEST_WAIT_FOR_TAB); // If the Service Worker push event handler does not show a notification, we @@ -552,9 +554,9 @@ PushEventNotificationWithoutEventWaitUntil) { std::string script_result; content::WebContents* web_contents = - browser()->tab_strip_model()->GetActiveWebContents(); + GetBrowser()->tab_strip_model()->GetActiveWebContents(); - TryToRegisterSuccessfully("1-0" /* expected_push_registration_id */); + TryToSubscribeSuccessfully("1-0" /* expected_push_subscription_id */); PushMessagingApplicationId app_id = GetServiceWorkerAppId(0LL); EXPECT_EQ(app_id.app_id_guid(), gcm_service()->last_registered_app_id()); @@ -608,11 +610,11 @@ ASSERT_TRUE(RunScript("registerServiceWorker()", &script_result)); ASSERT_EQ("ok - service worker registered", script_result); - InfoBarResponder accepting_responder(browser(), true); + InfoBarResponder accepting_responder(GetBrowser(), true); ASSERT_TRUE(RunScript("requestNotificationPermission();", &script_result)); EXPECT_EQ("permission status - granted", script_result); - ASSERT_TRUE(RunScript("registerPush()", &script_result)); + ASSERT_TRUE(RunScript("subscribePush()", &script_result)); EXPECT_EQ(std::string(kPushMessagingEndpoint) + " - 1-0", script_result); ASSERT_TRUE(RunScript("hasPermission()", &script_result)); @@ -625,11 +627,11 @@ ASSERT_TRUE(RunScript("registerServiceWorker()", &script_result)); ASSERT_EQ("ok - service worker registered", script_result); - InfoBarResponder cancelling_responder(browser(), false); + InfoBarResponder cancelling_responder(GetBrowser(), false); ASSERT_TRUE(RunScript("requestNotificationPermission();", &script_result)); EXPECT_EQ("permission status - denied", script_result); - ASSERT_TRUE(RunScript("registerPush()", &script_result)); + ASSERT_TRUE(RunScript("subscribePush()", &script_result)); EXPECT_EQ("AbortError - Registration failed - permission denied", script_result); @@ -637,56 +639,56 @@ EXPECT_EQ("permission status - denied", script_result); } -IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, UnregisterSuccess) { +IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, UnsubscribeSuccess) { std::string script_result; EXPECT_TRUE(RunScript("registerServiceWorker()", &script_result)); EXPECT_EQ("ok - service worker registered", script_result); // Resolves true if there was a subscription. - TryToRegisterSuccessfully("1-0" /* expected_push_registration_id */); + TryToSubscribeSuccessfully("1-0" /* expected_push_subscription_id */); gcm_service()->AddExpectedUnregisterResponse(gcm::GCMClient::SUCCESS); - ASSERT_TRUE(RunScript("unregister()", &script_result)); - EXPECT_EQ("unregister result: true", script_result); + ASSERT_TRUE(RunScript("unsubscribePush()", &script_result)); + EXPECT_EQ("unsubscribe result: true", script_result); // Resolves false if there was no longer a subscription. - ASSERT_TRUE(RunScript("unregister()", &script_result)); - EXPECT_EQ("unregister result: false", script_result); + ASSERT_TRUE(RunScript("unsubscribePush()", &script_result)); + EXPECT_EQ("unsubscribe result: false", script_result); // Doesn't reject if there was a network error (deactivates subscription // locally anyway). - TryToRegisterSuccessfully("1-1" /* expected_push_registration_id */); + TryToSubscribeSuccessfully("1-1" /* expected_push_subscription_id */); gcm_service()->AddExpectedUnregisterResponse(gcm::GCMClient::NETWORK_ERROR); - ASSERT_TRUE(RunScript("unregister()", &script_result)); - EXPECT_EQ("unregister result: true", script_result); - ASSERT_TRUE(RunScript("hasRegistration()", &script_result)); - EXPECT_EQ("false - not registered", script_result); + ASSERT_TRUE(RunScript("unsubscribePush()", &script_result)); + EXPECT_EQ("unsubscribe result: true", script_result); + ASSERT_TRUE(RunScript("hasSubscription()", &script_result)); + EXPECT_EQ("false - not subscribed", script_result); // Doesn't reject if there were other push service errors (deactivates // subscription locally anyway). - TryToRegisterSuccessfully("1-2" /* expected_push_registration_id */); + TryToSubscribeSuccessfully("1-2" /* expected_push_subscription_id */); gcm_service()->AddExpectedUnregisterResponse( gcm::GCMClient::INVALID_PARAMETER); - ASSERT_TRUE(RunScript("unregister()", &script_result)); - EXPECT_EQ("unregister result: true", script_result); + ASSERT_TRUE(RunScript("unsubscribePush()", &script_result)); + EXPECT_EQ("unsubscribe result: true", script_result); // Unsubscribing (with an existing reference to a PushSubscription), after // unregistering the Service Worker, just means push subscription isn't found. - TryToRegisterSuccessfully("1-3" /* expected_push_registration_id */); + TryToSubscribeSuccessfully("1-3" /* expected_push_subscription_id */); ASSERT_TRUE(RunScript("unregisterServiceWorker()", &script_result)); ASSERT_EQ("service worker unregistration status: true", script_result); - ASSERT_TRUE(RunScript("unregister()", &script_result)); - EXPECT_EQ("unregister result: false", script_result); + ASSERT_TRUE(RunScript("unsubscribePush()", &script_result)); + EXPECT_EQ("unsubscribe result: false", script_result); } IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, - GlobalResetPushPermissionUnregisters) { + GlobalResetPushPermissionUnsubscribes) { std::string script_result; - TryToRegisterSuccessfully("1-0" /* expected_push_registration_id */); + TryToSubscribeSuccessfully("1-0" /* expected_push_subscription_id */); - ASSERT_TRUE(RunScript("hasRegistration()", &script_result)); - EXPECT_EQ("true - registered", script_result); + ASSERT_TRUE(RunScript("hasSubscription()", &script_result)); + EXPECT_EQ("true - subscribed", script_result); ASSERT_TRUE(RunScript("hasPermission()", &script_result)); EXPECT_EQ("permission status - granted", script_result); @@ -696,7 +698,7 @@ push_service()->SetContentSettingChangedCallbackForTesting( message_loop_runner->QuitClosure()); - browser()->profile()->GetHostContentSettingsMap()-> + GetBrowser()->profile()->GetHostContentSettingsMap()-> ClearSettingsForOneType(CONTENT_SETTINGS_TYPE_PUSH_MESSAGING); message_loop_runner->Run(); @@ -704,18 +706,18 @@ ASSERT_TRUE(RunScript("hasPermission()", &script_result)); EXPECT_EQ("permission status - default", script_result); - ASSERT_TRUE(RunScript("hasRegistration()", &script_result)); - EXPECT_EQ("false - not registered", script_result); + ASSERT_TRUE(RunScript("hasSubscription()", &script_result)); + EXPECT_EQ("false - not subscribed", script_result); } IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, - LocalResetPushPermissionUnregisters) { + LocalResetPushPermissionUnsubscribes) { std::string script_result; - TryToRegisterSuccessfully("1-0" /* expected_push_registration_id */); + TryToSubscribeSuccessfully("1-0" /* expected_push_subscription_id */); - ASSERT_TRUE(RunScript("hasRegistration()", &script_result)); - EXPECT_EQ("true - registered", script_result); + ASSERT_TRUE(RunScript("hasSubscription()", &script_result)); + EXPECT_EQ("true - subscribed", script_result); ASSERT_TRUE(RunScript("hasPermission()", &script_result)); EXPECT_EQ("permission status - granted", script_result); @@ -726,7 +728,7 @@ message_loop_runner->QuitClosure()); GURL origin = https_server()->GetURL(std::string()).GetOrigin(); - browser()->profile()->GetHostContentSettingsMap()->SetContentSetting( + GetBrowser()->profile()->GetHostContentSettingsMap()->SetContentSetting( ContentSettingsPattern::FromURLNoWildcard(origin), ContentSettingsPattern::FromURLNoWildcard(origin), CONTENT_SETTINGS_TYPE_PUSH_MESSAGING, @@ -738,18 +740,18 @@ ASSERT_TRUE(RunScript("hasPermission()", &script_result)); EXPECT_EQ("permission status - default", script_result); - ASSERT_TRUE(RunScript("hasRegistration()", &script_result)); - EXPECT_EQ("false - not registered", script_result); + ASSERT_TRUE(RunScript("hasSubscription()", &script_result)); + EXPECT_EQ("false - not subscribed", script_result); } IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, - DenyPushPermissionUnregisters) { + DenyPushPermissionUnsubscribes) { std::string script_result; - TryToRegisterSuccessfully("1-0" /* expected_push_registration_id */); + TryToSubscribeSuccessfully("1-0" /* expected_push_subscription_id */); - ASSERT_TRUE(RunScript("hasRegistration()", &script_result)); - EXPECT_EQ("true - registered", script_result); + ASSERT_TRUE(RunScript("hasSubscription()", &script_result)); + EXPECT_EQ("true - subscribed", script_result); ASSERT_TRUE(RunScript("hasPermission()", &script_result)); EXPECT_EQ("permission status - granted", script_result); @@ -760,7 +762,7 @@ message_loop_runner->QuitClosure()); GURL origin = https_server()->GetURL(std::string()).GetOrigin(); - browser()->profile()->GetHostContentSettingsMap()->SetContentSetting( + GetBrowser()->profile()->GetHostContentSettingsMap()->SetContentSetting( ContentSettingsPattern::FromURLNoWildcard(origin), ContentSettingsPattern::FromURLNoWildcard(origin), CONTENT_SETTINGS_TYPE_PUSH_MESSAGING, @@ -772,18 +774,18 @@ ASSERT_TRUE(RunScript("hasPermission()", &script_result)); EXPECT_EQ("permission status - denied", script_result); - ASSERT_TRUE(RunScript("hasRegistration()", &script_result)); - EXPECT_EQ("false - not registered", script_result); + ASSERT_TRUE(RunScript("hasSubscription()", &script_result)); + EXPECT_EQ("false - not subscribed", script_result); } IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, - GlobalResetNotificationsPermissionUnregisters) { + GlobalResetNotificationsPermissionUnsubscribes) { std::string script_result; - TryToRegisterSuccessfully("1-0" /* expected_push_registration_id */); + TryToSubscribeSuccessfully("1-0" /* expected_push_subscription_id */); - ASSERT_TRUE(RunScript("hasRegistration()", &script_result)); - EXPECT_EQ("true - registered", script_result); + ASSERT_TRUE(RunScript("hasSubscription()", &script_result)); + EXPECT_EQ("true - subscribed", script_result); ASSERT_TRUE(RunScript("hasPermission()", &script_result)); EXPECT_EQ("permission status - granted", script_result); @@ -793,7 +795,7 @@ push_service()->SetContentSettingChangedCallbackForTesting( message_loop_runner->QuitClosure()); - browser()->profile()->GetHostContentSettingsMap()-> + GetBrowser()->profile()->GetHostContentSettingsMap()-> ClearSettingsForOneType(CONTENT_SETTINGS_TYPE_NOTIFICATIONS); message_loop_runner->Run(); @@ -801,18 +803,18 @@ ASSERT_TRUE(RunScript("hasPermission()", &script_result)); EXPECT_EQ("permission status - default", script_result); - ASSERT_TRUE(RunScript("hasRegistration()", &script_result)); - EXPECT_EQ("false - not registered", script_result); + ASSERT_TRUE(RunScript("hasSubscription()", &script_result)); + EXPECT_EQ("false - not subscribed", script_result); } IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, - LocalResetNotificationsPermissionUnregisters) { + LocalResetNotificationsPermissionUnsubscribes) { std::string script_result; - TryToRegisterSuccessfully("1-0" /* expected_push_registration_id */); + TryToSubscribeSuccessfully("1-0" /* expected_push_subscription_id */); - ASSERT_TRUE(RunScript("hasRegistration()", &script_result)); - EXPECT_EQ("true - registered", script_result); + ASSERT_TRUE(RunScript("hasSubscription()", &script_result)); + EXPECT_EQ("true - subscribed", script_result); ASSERT_TRUE(RunScript("hasPermission()", &script_result)); EXPECT_EQ("permission status - granted", script_result); @@ -823,7 +825,7 @@ message_loop_runner->QuitClosure()); GURL origin = https_server()->GetURL(std::string()).GetOrigin(); - browser()->profile()->GetHostContentSettingsMap()->SetContentSetting( + GetBrowser()->profile()->GetHostContentSettingsMap()->SetContentSetting( ContentSettingsPattern::FromURLNoWildcard(origin), ContentSettingsPattern::Wildcard(), CONTENT_SETTINGS_TYPE_NOTIFICATIONS, @@ -835,18 +837,18 @@ ASSERT_TRUE(RunScript("hasPermission()", &script_result)); EXPECT_EQ("permission status - default", script_result); - ASSERT_TRUE(RunScript("hasRegistration()", &script_result)); - EXPECT_EQ("false - not registered", script_result); + ASSERT_TRUE(RunScript("hasSubscription()", &script_result)); + EXPECT_EQ("false - not subscribed", script_result); } IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, - DenyNotificationsPermissionUnregisters) { + DenyNotificationsPermissionUnsubscribes) { std::string script_result; - TryToRegisterSuccessfully("1-0" /* expected_push_registration_id */); + TryToSubscribeSuccessfully("1-0" /* expected_push_subscription_id */); - ASSERT_TRUE(RunScript("hasRegistration()", &script_result)); - EXPECT_EQ("true - registered", script_result); + ASSERT_TRUE(RunScript("hasSubscription()", &script_result)); + EXPECT_EQ("true - subscribed", script_result); ASSERT_TRUE(RunScript("hasPermission()", &script_result)); EXPECT_EQ("permission status - granted", script_result); @@ -857,7 +859,7 @@ message_loop_runner->QuitClosure()); GURL origin = https_server()->GetURL(std::string()).GetOrigin(); - browser()->profile()->GetHostContentSettingsMap()->SetContentSetting( + GetBrowser()->profile()->GetHostContentSettingsMap()->SetContentSetting( ContentSettingsPattern::FromURLNoWildcard(origin), ContentSettingsPattern::Wildcard(), CONTENT_SETTINGS_TYPE_NOTIFICATIONS, @@ -869,18 +871,18 @@ ASSERT_TRUE(RunScript("hasPermission()", &script_result)); EXPECT_EQ("permission status - denied", script_result); - ASSERT_TRUE(RunScript("hasRegistration()", &script_result)); - EXPECT_EQ("false - not registered", script_result); + ASSERT_TRUE(RunScript("hasSubscription()", &script_result)); + EXPECT_EQ("false - not subscribed", script_result); } IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, - GrantAlreadyGrantedPermissionDoesNotUnregister) { + GrantAlreadyGrantedPermissionDoesNotUnsubscribe) { std::string script_result; - TryToRegisterSuccessfully("1-0" /* expected_push_registration_id */); + TryToSubscribeSuccessfully("1-0" /* expected_push_subscription_id */); - ASSERT_TRUE(RunScript("hasRegistration()", &script_result)); - EXPECT_EQ("true - registered", script_result); + ASSERT_TRUE(RunScript("hasSubscription()", &script_result)); + EXPECT_EQ("true - subscribed", script_result); ASSERT_TRUE(RunScript("hasPermission()", &script_result)); EXPECT_EQ("permission status - granted", script_result); @@ -891,13 +893,13 @@ base::BarrierClosure(2, message_loop_runner->QuitClosure())); GURL origin = https_server()->GetURL(std::string()).GetOrigin(); - browser()->profile()->GetHostContentSettingsMap()->SetContentSetting( + GetBrowser()->profile()->GetHostContentSettingsMap()->SetContentSetting( ContentSettingsPattern::FromURLNoWildcard(origin), ContentSettingsPattern::Wildcard(), CONTENT_SETTINGS_TYPE_NOTIFICATIONS, std::string(), CONTENT_SETTING_ALLOW); - browser()->profile()->GetHostContentSettingsMap()->SetContentSetting( + GetBrowser()->profile()->GetHostContentSettingsMap()->SetContentSetting( ContentSettingsPattern::FromURLNoWildcard(origin), ContentSettingsPattern::FromURLNoWildcard(origin), CONTENT_SETTINGS_TYPE_PUSH_MESSAGING, @@ -909,22 +911,22 @@ ASSERT_TRUE(RunScript("hasPermission()", &script_result)); EXPECT_EQ("permission status - granted", script_result); - ASSERT_TRUE(RunScript("hasRegistration()", &script_result)); - EXPECT_EQ("true - registered", script_result); + ASSERT_TRUE(RunScript("hasSubscription()", &script_result)); + EXPECT_EQ("true - subscribed", script_result); } // This test is testing some non-trivial content settings rules and make sure -// that they are respected with regards to automatic unregistration. In other -// words, it checks that the push service does not end up unregistering origins +// that they are respected with regards to automatic unsubscription. In other +// words, it checks that the push service does not end up unsubscribing origins // that have push permission with some non-common rules. IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, - AutomaticUnregistrationFollowsContentSettingRules) { + AutomaticUnsubscriptionFollowsContentSettingRules) { std::string script_result; - TryToRegisterSuccessfully("1-0" /* expected_push_registration_id */); + TryToSubscribeSuccessfully("1-0" /* expected_push_subscription_id */); - ASSERT_TRUE(RunScript("hasRegistration()", &script_result)); - EXPECT_EQ("true - registered", script_result); + ASSERT_TRUE(RunScript("hasSubscription()", &script_result)); + EXPECT_EQ("true - subscribed", script_result); ASSERT_TRUE(RunScript("hasPermission()", &script_result)); EXPECT_EQ("permission status - granted", script_result); @@ -935,25 +937,25 @@ base::BarrierClosure(4, message_loop_runner->QuitClosure())); GURL origin = https_server()->GetURL(std::string()).GetOrigin(); - browser()->profile()->GetHostContentSettingsMap()->SetContentSetting( + GetBrowser()->profile()->GetHostContentSettingsMap()->SetContentSetting( ContentSettingsPattern::Wildcard(), ContentSettingsPattern::Wildcard(), CONTENT_SETTINGS_TYPE_NOTIFICATIONS, std::string(), CONTENT_SETTING_ALLOW); - browser()->profile()->GetHostContentSettingsMap()->SetContentSetting( + GetBrowser()->profile()->GetHostContentSettingsMap()->SetContentSetting( ContentSettingsPattern::FromString("https://*"), ContentSettingsPattern::FromString("https://*"), CONTENT_SETTINGS_TYPE_PUSH_MESSAGING, std::string(), CONTENT_SETTING_ALLOW); - browser()->profile()->GetHostContentSettingsMap()->SetContentSetting( + GetBrowser()->profile()->GetHostContentSettingsMap()->SetContentSetting( ContentSettingsPattern::FromURLNoWildcard(origin), ContentSettingsPattern::Wildcard(), CONTENT_SETTINGS_TYPE_NOTIFICATIONS, std::string(), CONTENT_SETTING_DEFAULT); - browser()->profile()->GetHostContentSettingsMap()->SetContentSetting( + GetBrowser()->profile()->GetHostContentSettingsMap()->SetContentSetting( ContentSettingsPattern::FromURLNoWildcard(origin), ContentSettingsPattern::FromURLNoWildcard(origin), CONTENT_SETTINGS_TYPE_PUSH_MESSAGING, @@ -970,27 +972,27 @@ ASSERT_TRUE(RunScript("hasPermission()", &script_result)); EXPECT_EQ("permission status - granted", script_result); - ASSERT_TRUE(RunScript("hasRegistration()", &script_result)); - EXPECT_EQ("true - registered", script_result); + ASSERT_TRUE(RunScript("hasSubscription()", &script_result)); + EXPECT_EQ("true - subscribed", script_result); } // Checks that automatically unsubscribing due to a revoked permission is -// handled well if the sender ID needed to unregister was already deleted. +// handled well if the sender ID needed to unsubscribe was already deleted. IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, ResetPushPermissionAfterClearingSiteData) { std::string script_result; - TryToRegisterSuccessfully("1-0" /* expected_push_registration_id */); + TryToSubscribeSuccessfully("1-0" /* expected_push_subscription_id */); PushMessagingApplicationId app_id = GetServiceWorkerAppId(0LL); EXPECT_EQ(app_id.app_id_guid(), gcm_service()->last_registered_app_id()); PushMessagingApplicationId stored_app_id = PushMessagingApplicationId::Get( - browser()->profile(), app_id.app_id_guid()); + GetBrowser()->profile(), app_id.app_id_guid()); EXPECT_TRUE(stored_app_id.IsValid()); // Simulate a user clearing site data (including Service Workers, crucially). BrowsingDataRemover* remover = - BrowsingDataRemover::CreateForUnboundedRange(browser()->profile()); + BrowsingDataRemover::CreateForUnboundedRange(GetBrowser()->profile()); BrowsingDataRemoverCompletionObserver observer(remover); remover->Remove(BrowsingDataRemover::REMOVE_SITE_DATA, BrowsingDataHelper::UNPROTECTED_WEB); @@ -1003,14 +1005,46 @@ // This shouldn't (asynchronously) cause a DCHECK. // TODO(johnme): Get this test running on Android, which has a different - // codepath due to sender_id being required for unregistering there. - browser()->profile()->GetHostContentSettingsMap()-> + // codepath due to sender_id being required for unsubscribeing there. + GetBrowser()->profile()->GetHostContentSettingsMap()-> ClearSettingsForOneType(CONTENT_SETTINGS_TYPE_PUSH_MESSAGING); run_loop.Run(); // app_id should no longer be stored in prefs PushMessagingApplicationId stored_app_id2 = PushMessagingApplicationId::Get( - browser()->profile(), app_id.app_id_guid()); + GetBrowser()->profile(), app_id.app_id_guid()); EXPECT_FALSE(stored_app_id2.IsValid()); } + +class PushMessagingIncognitoBrowserTest : public PushMessagingBrowserTest { + public: + ~PushMessagingIncognitoBrowserTest() override {} + + // PushMessagingBrowserTest: + void SetUpOnMainThread() override { + incognito_browser_ = CreateIncognitoBrowser(); + PushMessagingBrowserTest::SetUpOnMainThread(); + } + + Browser* GetBrowser() const override { return incognito_browser_; } + + private: + Browser* incognito_browser_ = nullptr; +}; + +// Regression test for https://crbug.com/476474 +IN_PROC_BROWSER_TEST_F(PushMessagingIncognitoBrowserTest, + IncognitoGetSubscriptionDoesNotHang) { + ASSERT_TRUE(GetBrowser()->profile()->IsOffTheRecord()); + + std::string script_result; + + ASSERT_TRUE(RunScript("registerServiceWorker()", &script_result)); + ASSERT_EQ("ok - service worker registered", script_result); + + // In Incognito mode the promise returned by getSubscription should not hang, + // it should just fulfill with null. + ASSERT_TRUE(RunScript("hasSubscription()", &script_result)); + ASSERT_EQ("false - not subscribed", script_result); +}
diff --git a/chrome/browser/renderer_host/chrome_resource_dispatcher_host_delegate.cc b/chrome/browser/renderer_host/chrome_resource_dispatcher_host_delegate.cc index 3e494c81..3244504 100644 --- a/chrome/browser/renderer_host/chrome_resource_dispatcher_host_delegate.cc +++ b/chrome/browser/renderer_host/chrome_resource_dispatcher_host_delegate.cc
@@ -372,7 +372,7 @@ if (resource_type == content::RESOURCE_TYPE_MAIN_FRAME) { throttles->push_back( InterceptNavigationDelegate::CreateThrottleFor(request)); - } else if (resource_type == content::RESOURCE_TYPE_XHR) { + } else { InterceptNavigationDelegate::UpdateUserGestureCarryoverInfo(request); } }
diff --git a/chrome/browser/renderer_host/pepper/pepper_output_protection_message_filter.cc b/chrome/browser/renderer_host/pepper/pepper_output_protection_message_filter.cc index ef8480b..9af20a8c 100644 --- a/chrome/browser/renderer_host/pepper/pepper_output_protection_message_filter.cc +++ b/chrome/browser/renderer_host/pepper/pepper_output_protection_message_filter.cc
@@ -75,6 +75,10 @@ *display_id = display.id(); return true; } + +void DoNothing(bool status) { +} + #endif } // namespace @@ -85,6 +89,12 @@ class PepperOutputProtectionMessageFilter::Delegate : public aura::WindowObserver { public: + typedef base::Callback<void(int32_t /* result */, + uint32_t /* link_mask */, + uint32_t /* protection_mask*/)> + QueryStatusCallback; + typedef base::Callback<void(int32_t /* result */)> EnableProtectionCallback; + Delegate(int render_process_id, int render_frame_id); ~Delegate() override; @@ -93,12 +103,19 @@ const aura::WindowObserver::HierarchyChangeParams& params) override; void OnWindowDestroying(aura::Window* window) override; - int32_t OnQueryStatus(uint32_t* link_mask, uint32_t* protection_mask); - int32_t OnEnableProtection(uint32_t desired_method_mask); + void QueryStatus(const QueryStatusCallback& callback); + void EnableProtection(uint32_t desired_method_mask, + const EnableProtectionCallback& callback); private: ui::DisplayConfigurator::ContentProtectionClientId GetClientId(); + void QueryStatusComplete( + const QueryStatusCallback& callback, + const ui::DisplayConfigurator::QueryProtectionResponse& response); + void EnableProtectionComplete(const EnableProtectionCallback& callback, + bool success); + // Used to lookup the WebContents associated with this PP_Instance. int render_process_id_; int render_frame_id_; @@ -114,6 +131,9 @@ // The last desired method mask. Will enable this mask on new display if // renderer changes display. uint32_t desired_method_mask_; + + base::WeakPtrFactory<PepperOutputProtectionMessageFilter::Delegate> + weak_ptr_factory_; }; PepperOutputProtectionMessageFilter::Delegate::Delegate(int render_process_id, @@ -122,7 +142,8 @@ render_frame_id_(render_frame_id), window_(NULL), client_id_(ui::DisplayConfigurator::kInvalidClientId), - display_id_(0) { + display_id_(0), + weak_ptr_factory_(this) { DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); } @@ -158,25 +179,56 @@ return client_id_; } -int32_t PepperOutputProtectionMessageFilter::Delegate::OnQueryStatus( - uint32_t* link_mask, - uint32_t* protection_mask) { +void PepperOutputProtectionMessageFilter::Delegate::QueryStatus( + const QueryStatusCallback& callback) { DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); content::RenderFrameHost* rfh = content::RenderFrameHost::FromID(render_process_id_, render_frame_id_); if (!rfh) { LOG(WARNING) << "RenderFrameHost is not alive."; - return PP_ERROR_FAILED; + callback.Run(PP_ERROR_FAILED, 0, 0); + return; } ui::DisplayConfigurator* configurator = ash::Shell::GetInstance()->display_configurator(); - bool result = configurator->QueryContentProtectionStatus( - GetClientId(), display_id_, link_mask, protection_mask); + configurator->QueryContentProtectionStatus( + GetClientId(), display_id_, + base::Bind( + &PepperOutputProtectionMessageFilter::Delegate::QueryStatusComplete, + weak_ptr_factory_.GetWeakPtr(), callback)); +} +void PepperOutputProtectionMessageFilter::Delegate::EnableProtection( + uint32_t desired_method_mask, + const EnableProtectionCallback& callback) { + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + + ui::DisplayConfigurator* configurator = + ash::Shell::GetInstance()->display_configurator(); + configurator->EnableContentProtection( + GetClientId(), display_id_, desired_method_mask, + base::Bind(&PepperOutputProtectionMessageFilter::Delegate:: + EnableProtectionComplete, + weak_ptr_factory_.GetWeakPtr(), callback)); + desired_method_mask_ = desired_method_mask; +} + +void PepperOutputProtectionMessageFilter::Delegate::QueryStatusComplete( + const QueryStatusCallback& callback, + const ui::DisplayConfigurator::QueryProtectionResponse& response) { + content::RenderFrameHost* rfh = + content::RenderFrameHost::FromID(render_process_id_, render_frame_id_); + if (!rfh) { + LOG(WARNING) << "RenderFrameHost is not alive."; + callback.Run(PP_ERROR_FAILED, 0, 0); + return; + } + + uint32_t link_mask = response.link_mask; // If we successfully retrieved the device level status, check for capturers. - if (result) { + if (response.success) { const bool capture_detected = // Check for tab capture on the current tab. content::WebContents::FromRenderFrameHost(rfh)->GetCapturerCount() > @@ -185,22 +237,17 @@ MediaCaptureDevicesDispatcher::GetInstance() ->IsDesktopCaptureInProgress(); if (capture_detected) - *link_mask |= ui::DISPLAY_CONNECTION_TYPE_NETWORK; + link_mask |= ui::DISPLAY_CONNECTION_TYPE_NETWORK; } - return result ? PP_OK : PP_ERROR_FAILED; + callback.Run(response.success ? PP_OK : PP_ERROR_FAILED, link_mask, + response.protection_mask); } -int32_t PepperOutputProtectionMessageFilter::Delegate::OnEnableProtection( - uint32_t desired_method_mask) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); - - ui::DisplayConfigurator* configurator = - ash::Shell::GetInstance()->display_configurator(); - bool result = configurator->EnableContentProtection( - GetClientId(), display_id_, desired_method_mask); - desired_method_mask_ = desired_method_mask; - return result ? PP_OK : PP_ERROR_FAILED; +void PepperOutputProtectionMessageFilter::Delegate::EnableProtectionComplete( + const EnableProtectionCallback& callback, + bool result) { + callback.Run(result ? PP_OK : PP_ERROR_FAILED); } void PepperOutputProtectionMessageFilter::Delegate::OnWindowHierarchyChanged( @@ -222,10 +269,12 @@ // Display changed and should enable output protections on new display. ui::DisplayConfigurator* configurator = ash::Shell::GetInstance()->display_configurator(); - configurator->EnableContentProtection( - GetClientId(), new_display_id, desired_method_mask_); - configurator->EnableContentProtection( - GetClientId(), display_id_, ui::CONTENT_PROTECTION_METHOD_NONE); + configurator->EnableContentProtection(GetClientId(), new_display_id, + desired_method_mask_, + base::Bind(&DoNothing)); + configurator->EnableContentProtection(GetClientId(), display_id_, + ui::CONTENT_PROTECTION_METHOD_NONE, + base::Bind(&DoNothing)); } display_id_ = new_display_id; } @@ -240,7 +289,8 @@ PepperOutputProtectionMessageFilter::PepperOutputProtectionMessageFilter( content::BrowserPpapiHost* host, - PP_Instance instance) { + PP_Instance instance) + : weak_ptr_factory_(this) { #if defined(OS_CHROMEOS) DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); int render_process_id = 0; @@ -283,15 +333,11 @@ int32_t PepperOutputProtectionMessageFilter::OnQueryStatus( ppapi::host::HostMessageContext* context) { #if defined(OS_CHROMEOS) - uint32_t link_mask = 0, protection_mask = 0; - int32_t result = delegate_->OnQueryStatus(&link_mask, &protection_mask); - ppapi::host::ReplyMessageContext reply_context = context->MakeReplyMessageContext(); - reply_context.params.set_result(result); - SendReply(reply_context, - PpapiPluginMsg_OutputProtection_QueryStatusReply(link_mask, - protection_mask)); + delegate_->QueryStatus( + base::Bind(&PepperOutputProtectionMessageFilter::OnQueryStatusComplete, + weak_ptr_factory_.GetWeakPtr(), reply_context)); return PP_OK_COMPLETIONPENDING; #else NOTIMPLEMENTED(); @@ -305,10 +351,11 @@ #if defined(OS_CHROMEOS) ppapi::host::ReplyMessageContext reply_context = context->MakeReplyMessageContext(); - int32_t result = delegate_->OnEnableProtection(desired_method_mask); - reply_context.params.set_result(result); - SendReply(reply_context, - PpapiPluginMsg_OutputProtection_EnableProtectionReply()); + delegate_->EnableProtection( + desired_method_mask, + base::Bind( + &PepperOutputProtectionMessageFilter::OnEnableProtectionComplete, + weak_ptr_factory_.GetWeakPtr(), reply_context)); return PP_OK_COMPLETIONPENDING; #else NOTIMPLEMENTED(); @@ -316,4 +363,22 @@ #endif } +void PepperOutputProtectionMessageFilter::OnQueryStatusComplete( + ppapi::host::ReplyMessageContext reply_context, + int32_t result, + uint32_t link_mask, + uint32_t protection_mask) { + reply_context.params.set_result(result); + SendReply(reply_context, PpapiPluginMsg_OutputProtection_QueryStatusReply( + link_mask, protection_mask)); +} + +void PepperOutputProtectionMessageFilter::OnEnableProtectionComplete( + ppapi::host::ReplyMessageContext reply_context, + int32_t result) { + reply_context.params.set_result(result); + SendReply(reply_context, + PpapiPluginMsg_OutputProtection_EnableProtectionReply()); +} + } // namespace chrome
diff --git a/chrome/browser/renderer_host/pepper/pepper_output_protection_message_filter.h b/chrome/browser/renderer_host/pepper/pepper_output_protection_message_filter.h index c9e575e..bb6004f 100644 --- a/chrome/browser/renderer_host/pepper/pepper_output_protection_message_filter.h +++ b/chrome/browser/renderer_host/pepper/pepper_output_protection_message_filter.h
@@ -5,6 +5,7 @@ #ifndef CHROME_BROWSER_RENDERER_HOST_PEPPER_PEPPER_OUTPUT_PROTECTION_MESSAGE_FILTER_H_ #define CHROME_BROWSER_RENDERER_HOST_PEPPER_PEPPER_OUTPUT_PROTECTION_MESSAGE_FILTER_H_ +#include "base/memory/weak_ptr.h" #include "ppapi/c/pp_instance.h" #include "ppapi/host/resource_message_filter.h" @@ -44,11 +45,22 @@ int32_t OnEnableProtection(ppapi::host::HostMessageContext* context, uint32_t desired_method_mask); + void OnQueryStatusComplete(ppapi::host::ReplyMessageContext reply_context, + int32_t result, + uint32_t link_mask, + uint32_t protection_mask); + + void OnEnableProtectionComplete( + ppapi::host::ReplyMessageContext reply_context, + int32_t result); + #if defined(OS_CHROMEOS) // Delegator. Should be deleted in UI thread. Delegate* delegate_; #endif + base::WeakPtrFactory<PepperOutputProtectionMessageFilter> weak_ptr_factory_; + DISALLOW_COPY_AND_ASSIGN(PepperOutputProtectionMessageFilter); };
diff --git a/chrome/browser/resources/chromeos/login/header_bar.js b/chrome/browser/resources/chromeos/login/header_bar.js index cc718ce8..6a07090 100644 --- a/chrome/browser/resources/chromeos/login/header_bar.js +++ b/chrome/browser/resources/chromeos/login/header_bar.js
@@ -327,12 +327,24 @@ (Oobe.getInstance().displayType == DISPLAY_TYPE.USER_ADDING); var isLockScreen = (Oobe.getInstance().displayType == DISPLAY_TYPE.LOCK); + var isNewGaiaScreenWithBackButton = + gaiaIsActive && + this.isNewGaiaFlow_ && + !($('back-button-item').hidden); + var supervisedUserCreationDialogIsActiveAndNotIntro = + supervisedUserCreationDialogIsActive && + $('supervised-user-creation').currentPage_ != 'intro'; $('add-user-button').hidden = - !accountPickerIsActive || isMultiProfilesUI || isLockScreen; - $('more-settings-header-bar-item').hidden = !this.isNewGaiaFlow_ || + (!this.isNewGaiaFlow_ && !accountPickerIsActive) || + (this.isNewGaiaFlow_ && gaiaIsActive) || + isMultiProfilesUI || + isLockScreen || + supervisedUserCreationDialogIsActiveAndNotIntro; + $('more-settings-header-bar-item').hidden = !this.showCreateSupervised_ || - !accountPickerIsActive; + isNewGaiaScreenWithBackButton || + supervisedUserCreationDialogIsActive; $('cancel-add-user-button').hidden = (gaiaIsActive && this.isNewGaiaFlow_) || accountPickerIsActive || @@ -341,11 +353,12 @@ isMultiProfilesUI; $('guest-user-header-bar-item').hidden = (gaiaIsActive && !this.isNewGaiaFlow_) || - supervisedUserCreationDialogIsActive || + supervisedUserCreationDialogIsActiveAndNotIntro || !this.showGuest_ || wrongHWIDWarningIsActive || isSamlPasswordConfirm || - isMultiProfilesUI; + isMultiProfilesUI || + isNewGaiaScreenWithBackButton; $('restart-header-bar-item').hidden = !this.showReboot_; $('shutdown-header-bar-item').hidden = !this.showShutdown_; $('sign-out-user-item').hidden = !isLockScreen;
diff --git a/chrome/browser/resources/chromeos/login/screen_gaia_signin.js b/chrome/browser/resources/chromeos/login/screen_gaia_signin.js index 9e1bfef..2da77e3 100644 --- a/chrome/browser/resources/chromeos/login/screen_gaia_signin.js +++ b/chrome/browser/resources/chromeos/login/screen_gaia_signin.js
@@ -11,6 +11,11 @@ // lazy portal check should be fired. /** @const */ var GAIA_LOADING_PORTAL_SUSSPECT_TIME_SEC = 7; + // GAIA animation guard timer. Started when GAIA page is loaded + // (Authenticator 'ready' event) and is intended to guard against edge cases + // when 'showView' message is not generated/received. + /** @const */ var GAIA_ANIMATION_GUARD_MILLISEC = 300; + // Maximum Gaia loading time in seconds. /** @const */ var MAX_GAIA_LOADING_TIME_SEC = 60; @@ -78,6 +83,22 @@ loadingTimer_: undefined, /** + * Timer id of a guard timer that is fired in case 'showView' message + * is not received from GAIA. + * @type {number} + * @private + */ + loadAnimationGuardTimer_: undefined, + + /** + * Whether we've processed 'showView' message - either from GAIA or from + * guard timer. + * @type {boolean} + * @private + */ + showViewProcessed_: undefined, + + /** * Whether user can cancel Gaia screen. * @type {boolean} * @private @@ -220,6 +241,7 @@ $('enterprise-info-container').hidden = show; $('gaia-signin-divider').hidden = show; this.classList.toggle('loading', show); + $('signin-frame').classList.remove('show'); if (!show) this.classList.remove('auth-completed'); }, @@ -232,7 +254,7 @@ if (this != Oobe.getInstance().currentScreen) return; chrome.send('showLoadingTimeoutError'); - this.loadingTimer_ = window.setTimeout( + this.loadingTimer_ = setTimeout( this.onLoadingTimeOut_.bind(this), (MAX_GAIA_LOADING_TIME_SEC - GAIA_LOADING_PORTAL_SUSSPECT_TIME_SEC) * 1000); @@ -253,7 +275,7 @@ */ clearLoadingTimer_: function() { if (this.loadingTimer_) { - window.clearTimeout(this.loadingTimer_); + clearTimeout(this.loadingTimer_); this.loadingTimer_ = undefined; } }, @@ -264,12 +286,43 @@ */ startLoadingTimer_: function() { this.clearLoadingTimer_(); - this.loadingTimer_ = window.setTimeout( + this.loadingTimer_ = setTimeout( this.onLoadingSuspiciouslyLong_.bind(this), GAIA_LOADING_PORTAL_SUSSPECT_TIME_SEC * 1000); }, /** + * Handler for GAIA animation guard timer. + * @private + */ + onLoadAnimationGuardTimer_: function() { + this.loadAnimationGuardTimer_ = undefined; + this.onShowView_(); + }, + + /** + * Clears GAIA animation guard timer. + * @private + */ + clearLoadAnimationGuardTimer_: function() { + if (this.loadAnimationGuardTimer_) { + clearTimeout(this.loadAnimationGuardTimer_); + this.loadAnimationGuardTimer_ = undefined; + } + }, + + /** + * Sets up GAIA animation guard timer. + * @private + */ + startLoadAnimationGuardTimer_: function() { + this.clearLoadAnimationGuardTimer_(); + this.loadAnimationGuardTimer_ = setTimeout( + this.onLoadAnimationGuardTimer_.bind(this), + GAIA_ANIMATION_GUARD_MILLISEC); + }, + + /** * Whether Gaia is loading. * @type {boolean} */ @@ -496,17 +549,15 @@ * @private */ onAuthReady_: function() { - this.loading = false; + showViewProcessed_ = false; + if (this.isNewGaiaFlow) + this.startLoadAnimationGuardTimer_(); + this.clearLoadingTimer_(); + this.loading = false; - // Show deferred error bubble. - if (this.errorBubble_) { - this.showErrorBubble(this.errorBubble_[0], this.errorBubble_[1]); - this.errorBubble_ = undefined; - } - - chrome.send('loginWebuiReady'); - chrome.send('loginVisible', ['gaia-signin']); + if (!this.isNewGaiaFlow) + this.onLoginUIVisible_(); // Warm up the user images screen. Oobe.getInstance().preloadScreen({id: SCREEN_USER_IMAGE_PICKER}); @@ -536,14 +587,37 @@ */ onBackButton_: function(e) { $('back-button-item').hidden = !e.detail; + $('login-header-bar').updateUI_(); }, /** - * Invoked when the auth host emits 'showView' event. + * Invoked when the auth host emits 'showView' event or when corresponding + * guard time fires. * @private */ onShowView_: function(e) { + if (showViewProcessed_) + return; + + showViewProcessed_ = true; + this.clearLoadAnimationGuardTimer_(); $('signin-frame').classList.add('show'); + this.onLoginUIVisible_(); + }, + + /** + * Called when UI is shown. + * @private + */ + onLoginUIVisible_: function() { + // Show deferred error bubble. + if (this.errorBubble_) { + this.showErrorBubble(this.errorBubble_[0], this.errorBubble_[1]); + this.errorBubble_ = undefined; + } + + chrome.send('loginWebuiReady'); + chrome.send('loginVisible', ['gaia-signin']); }, /**
diff --git a/chrome/browser/resources/chromeos/login/screen_supervised_user_creation.js b/chrome/browser/resources/chromeos/login/screen_supervised_user_creation.js index d3a7f89..be8f355 100644 --- a/chrome/browser/resources/chromeos/login/screen_supervised_user_creation.js +++ b/chrome/browser/resources/chromeos/login/screen_supervised_user_creation.js
@@ -1123,7 +1123,7 @@ var pagesWithCancel = ['intro', 'manager', 'username', 'import-password', 'error', 'import']; $('login-header-bar').allowCancel = - pagesWithCancel.indexOf(visiblePage) > 0; + pagesWithCancel.indexOf(visiblePage) > -1; $('cancel-add-user-button').disabled = false; this.getScreenElement('import-link').hidden = true; @@ -1139,6 +1139,7 @@ 'password-error'); if (this.managerList_.pods.length > 0) this.managerList_.selectPod(this.managerList_.pods[0]); + $('login-header-bar').updateUI_(); } if (visiblePage == 'username' || visiblePage == 'import-password') { @@ -1342,6 +1343,8 @@ var notSignedInPages = ['intro', 'manager']; var postCreationPages = ['created']; if (notSignedInPages.indexOf(this.currentPage_) >= 0) { + chrome.send('hideLocalSupervisedUserCreation'); + // Make sure no manager password is kept: this.managerList_.clearPods();
diff --git a/chrome/browser/resources/chromeos/login/throbber_notice.css b/chrome/browser/resources/chromeos/login/throbber_notice.css new file mode 100644 index 0000000..8086a06 --- /dev/null +++ b/chrome/browser/resources/chromeos/login/throbber_notice.css
@@ -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. + */ + +paper-spinner::shadow .circle { + border-color: rgb(66, 133, 244); +} + +paper-spinner { + height: 36px; + width: 36px; +} + +#spinner-container { + margin-bottom: 25px; +} + +#spinner-comment { + color: grey; +}
diff --git a/chrome/browser/resources/chromeos/login/throbber_notice.html b/chrome/browser/resources/chromeos/login/throbber_notice.html index 0844dd2..243432de 100644 --- a/chrome/browser/resources/chromeos/login/throbber_notice.html +++ b/chrome/browser/resources/chromeos/login/throbber_notice.html
@@ -3,25 +3,10 @@ <polymer-element name="throbber-notice" attributes="text" noscript> <template> - <style> - paper-spinner::shadow .circle { - border-color: #4285f4; - } - - paper-spinner { - height: 36px; - width: 36px; - } - #spinner-container { - margin-bottom: 25px; - } - #spinner-comment { - color: grey; - } - </style> + <link rel="stylesheet" href="throbber_notice.css"> <div id="spinner-container" vertical layout center> - <paper-spinner active></paper-spinner> + <paper-spinner dir="ltr" active></paper-spinner> </div> <div id="spinner-comment">{{text}}</div> </template> -</polymer-element> \ No newline at end of file +</polymer-element>
diff --git a/chrome/browser/resources/component_extension_resources.grd b/chrome/browser/resources/component_extension_resources.grd index d5f5374..7e2b0344 100644 --- a/chrome/browser/resources/component_extension_resources.grd +++ b/chrome/browser/resources/component_extension_resources.grd
@@ -174,6 +174,7 @@ <include name="IDR_PDF_VIEWPORT_SCROLLER_JS" file="pdf/viewport_scroller.js" type="BINDATA" /> <include name="IDR_PDF_PDF_SCRIPTING_API_JS" file="pdf/pdf_scripting_api.js" type="BINDATA" /> <include name="IDR_PDF_ZOOM_MANAGER_JS" file="pdf/zoom_manager.js" type="BINDATA" /> + <include name="IDR_PDF_BROWSER_API_JS" file="pdf/browser_api.js" type="BINDATA" /> <include name="IDR_PDF_CONTENT_SCRIPT_JS" file="pdf/content_script.js" type="BINDATA" /> <include name="IDR_PDF_VIEWER_BOOKMARK_CSS" file="pdf/elements/viewer-bookmark/viewer-bookmark.css" type="BINDATA" />
diff --git a/chrome/browser/resources/extensions/extension_error.js b/chrome/browser/resources/extensions/extension_error.js index 6e3418d..7b9b7a1 100644 --- a/chrome/browser/resources/extensions/extension_error.js +++ b/chrome/browser/resources/extensions/extension_error.js
@@ -37,7 +37,7 @@ function ExtensionError(error, boundary) { var div = cloneTemplate('extension-error-metadata'); div.__proto__ = ExtensionError.prototype; - div.decorate(error, boundary); + div.decorateWithError_(error, boundary); return div; } @@ -51,12 +51,12 @@ /** * @param {(RuntimeError|ManifestError)} error The error the element should - * represent + * represent. * @param {Element} boundary The boundary for the FocusGrid. - * @override + * @private */ - decorate: function(error, boundary) { - cr.ui.FocusRow.prototype.decorate.call(this, boundary); + decorateWithError_: function(error, boundary) { + this.decorate(boundary); // Add an additional class for the severity level. if (error.type == chrome.developerPrivate.ErrorType.RUNTIME) {
diff --git a/chrome/browser/resources/extensions/extension_list.js b/chrome/browser/resources/extensions/extension_list.js index bb126d5..fe1cec3 100644 --- a/chrome/browser/resources/extensions/extension_list.js +++ b/chrome/browser/resources/extensions/extension_list.js
@@ -245,6 +245,7 @@ case EventType.LOADED: case EventType.UNLOADED: case EventType.ERROR_ADDED: + case EventType.PREFS_CHANGED: if (eventData.extensionInfo) this.updateExtension_(eventData.extensionInfo); break; @@ -666,14 +667,14 @@ !extension.optionsPage.openInTab); // The 'View in Web Store/View Web Site' link. - var siteLinkEnabled = !!extension.homepageUrl && + var siteLinkEnabled = !!extension.homePage.url && !this.enableAppInfoDialog_; this.updateVisibility_(row, '.site-link', siteLinkEnabled, function(item) { - item.href = extension.homepageUrl; + item.href = extension.homePage.url; item.textContent = loadTimeData.getString( - extension.homepageProvided ? 'extensionSettingsVisitWebsite' : - 'extensionSettingsVisitWebStore'); + extension.homePage.specified ? 'extensionSettingsVisitWebsite' : + 'extensionSettingsVisitWebStore'); }); var isUnpacked =
diff --git a/chrome/browser/resources/extensions/extensions.js b/chrome/browser/resources/extensions/extensions.js index f1843d312..06b62379 100644 --- a/chrome/browser/resources/extensions/extensions.js +++ b/chrome/browser/resources/extensions/extensions.js
@@ -150,6 +150,9 @@ extensionList.updateFocusableElements(); chrome.developerPrivate.updateProfileConfiguration( {inDeveloperMode: e.target.checked}); + var suffix = $('toggle-dev-on').checked ? 'Enabled' : 'Disabled'; + chrome.send('metricsHandler:recordAction', + ['Options_ToggleDeveloperMode_' + suffix]); }.bind(this)); window.addEventListener('resize', function() { @@ -158,6 +161,8 @@ // Set up the three dev mode buttons (load unpacked, pack and update). $('load-unpacked').addEventListener('click', function(e) { + chrome.send('metricsHandler:recordAction', + ['Options_LoadUnpackedExtension']); extensionLoader.loadUnpacked(); }); $('pack-extension').addEventListener('click', @@ -187,6 +192,16 @@ extensions.ExtensionOptionsOverlay.getInstance().initializePage( extensions.ExtensionSettings.showOverlay); + // Add user action logging for bottom links. + var moreExtensionLink = + document.getElementsByClassName('more-extensions-link'); + for (var i = 0; i < moreExtensionLink.length; i++) { + moreExtensionLink[i].addEventListener('click', function(e) { + chrome.send('metricsHandler:recordAction', + ['Options_GetMoreExtensions']); + }); + } + // Initialize the kiosk overlay. if (cr.isChromeOS) { var kioskOverlay = extensions.KioskAppsOverlay.getInstance(); @@ -304,6 +319,8 @@ */ handleUpdateExtensionNow_: function(e) { chrome.developerPrivate.autoUpdate(); + chrome.send('metricsHandler:recordAction', + ['Options_UpdateExtensions']); }, /**
diff --git a/chrome/browser/resources/history/history.css b/chrome/browser/resources/history/history.css index 7a356bb..a1d191b 100644 --- a/chrome/browser/resources/history/history.css +++ b/chrome/browser/resources/history/history.css
@@ -417,6 +417,11 @@ border-radius: 2px; } +.entry-box > div, +.site-domain-row > div { + min-width: 0; +} + .active :-webkit-any(.entry-box, .site-domain-row) { background-color: rgba(0, 0, 0, .05); }
diff --git a/chrome/browser/resources/net_internals/bandwidth_view.js b/chrome/browser/resources/net_internals/bandwidth_view.js index d5c7c62..2c4767a 100644 --- a/chrome/browser/resources/net_internals/bandwidth_view.js +++ b/chrome/browser/resources/net_internals/bandwidth_view.js
@@ -220,6 +220,7 @@ */ updateDataReductionProxyConfig_: function() { $(BandwidthView.PROXY_CONFIG_ID).innerHTML = ''; + $(BandwidthView.BYPASS_STATE_ID).innerHTML = ''; setNodeDisplay($(BandwidthView.BYPASS_STATE_CONTAINER_ID), false); if (this.data_reduction_proxy_config_) { @@ -257,8 +258,6 @@ if (hasBypassedProxy) { this.createEventTable_(this.last_bypass_.params, $(BandwidthView.BYPASS_STATE_ID)); - } else { - $(BandwidthView.BYPASS_STATE_ID).innerHtml = ''; } this.createEventTable_(this.data_reduction_proxy_config_,
diff --git a/chrome/browser/resources/net_internals/browser_bridge.js b/chrome/browser/resources/net_internals/browser_bridge.js index 20315821..2b976a4c 100644 --- a/chrome/browser/resources/net_internals/browser_bridge.js +++ b/chrome/browser/resources/net_internals/browser_bridge.js
@@ -215,8 +215,8 @@ this.send('enableIPv6'); }, - setLogLevel: function(logLevel) { - this.send('setLogLevel', ['' + logLevel]); + setCaptureMode: function(captureMode) { + this.send('setCaptureMode', ['' + captureMode]); }, importONCFile: function(fileContent, passcode) {
diff --git a/chrome/browser/resources/net_internals/capture_view.js b/chrome/browser/resources/net_internals/capture_view.js index 4b0a20f6..244dc1ce 100644 --- a/chrome/browser/resources/net_internals/capture_view.js +++ b/chrome/browser/resources/net_internals/capture_view.js
@@ -85,7 +85,7 @@ var byteLoggingCheckbox = $(CaptureView.BYTE_LOGGING_CHECKBOX_ID); if (byteLoggingCheckbox.checked) { - g_browser.setLogLevel(LogLevelType.LOG_ALL); + g_browser.setCaptureMode('IncludeSocketBytes'); // Once we enable byte logging, all bets are off on what gets captured. // Have the export view warn that the "strip cookies" option is @@ -97,7 +97,7 @@ // reload. ExportView.getInstance().showPrivacyWarning(); } else { - g_browser.setLogLevel(LogLevelType.LOG_ALL_BUT_BYTES); + g_browser.setCaptureMode('IncludeCookiesAndCredentials'); } },
diff --git a/chrome/browser/resources/net_internals/main.js b/chrome/browser/resources/net_internals/main.js index 0b49ad7..ab87b14 100644 --- a/chrome/browser/resources/net_internals/main.js +++ b/chrome/browser/resources/net_internals/main.js
@@ -12,7 +12,6 @@ var EventPhase = null; var EventSourceType = null; var EventSourceTypeNames = null; -var LogLevelType = null; var ClientInfo = null; var NetError = null; var QuicError = null; @@ -300,7 +299,6 @@ EventPhase = Constants.logEventPhase; EventSourceType = Constants.logSourceType; EventSourceTypeNames = makeInverseMap(EventSourceType); - LogLevelType = Constants.logLevelType; ClientInfo = Constants.clientInfo; LoadFlag = Constants.loadFlag; NetError = Constants.netError; @@ -331,7 +329,6 @@ typeof(receivedConstants.clientInfo) == 'object' && typeof(receivedConstants.logEventPhase) == 'object' && typeof(receivedConstants.logSourceType) == 'object' && - typeof(receivedConstants.logLevelType) == 'object' && typeof(receivedConstants.loadFlag) == 'object' && typeof(receivedConstants.netError) == 'object' && typeof(receivedConstants.addressFamily) == 'object' &&
diff --git a/chrome/browser/resources/options/autofill_options_list.js b/chrome/browser/resources/options/autofill_options_list.js index 004f6213..7db223b 100644 --- a/chrome/browser/resources/options/autofill_options_list.js +++ b/chrome/browser/resources/options/autofill_options_list.js
@@ -495,11 +495,9 @@ AutofillValuesList.prototype = { __proto__: InlineEditableItemList.prototype, - /** - * @override - * @param {string} entry - */ + /** @override */ createItem: function(entry) { + assert(entry === null || typeof entry == 'string'); return new ValuesListItem(this, entry); }, @@ -540,10 +538,11 @@ /** * @override - * @param {Array<string>} entry + * @param {?string|Array<string>} entry */ createItem: function(entry) { - return new NameListItem(this, entry); + var arrayOrNull = entry ? assertInstanceof(entry, Array) : null; + return new NameListItem(this, arrayOrNull); }, };
diff --git a/chrome/browser/resources/options/browser_options.html b/chrome/browser/resources/options/browser_options.html index 5cbe5c4..2f6aad4 100644 --- a/chrome/browser/resources/options/browser_options.html +++ b/chrome/browser/resources/options/browser_options.html
@@ -241,13 +241,17 @@ <if expr="not chromeos"> <div id="profiles-enable-guest" class="checkbox"> <label> - <input pref="profile.browser_guest_enabled" type="checkbox"> + <input pref="profile.browser_guest_enabled" + type="checkbox" + metric="Options_BrowserGuestEnabled"> <span i18n-content="profileBrowserGuestEnable"></span> </label> </div> <div id="profiles-enable-add-person" class="checkbox"> <label> - <input pref="profile.add_person_enabled" type="checkbox"> + <input pref="profile.add_person_enabled" + type="checkbox" + metric="Options_AddPersonEnabled"> <span i18n-content="profileAddPersonEnable"></span> </label> </div>
diff --git a/chrome/browser/resources/options/browser_options.js b/chrome/browser/resources/options/browser_options.js index b161c32..f104402a 100644 --- a/chrome/browser/resources/options/browser_options.js +++ b/chrome/browser/resources/options/browser_options.js
@@ -204,8 +204,6 @@ networkIndicator.setAttribute('controlled-by', 'shared'); networkIndicator.location = cr.ui.ArrowLocation.TOP_START; } - options.network.NetworkList.refreshNetworkData( - loadTimeData.getValue('networkData')); } // On Startup section. @@ -345,6 +343,8 @@ profilesList.addEventListener('change', this.setProfileViewButtonsStatus_); $('profiles-create').onclick = function(event) { + chrome.send('metricsHandler:recordAction', + ['Options_ShowCreateProfileDlg']); ManageProfileOverlay.showCreateDialog(); }; if (OptionsPage.isSettingsApp()) { @@ -354,12 +354,17 @@ }; } $('profiles-manage').onclick = function(event) { + chrome.send('metricsHandler:recordAction', + ['Options_ShowEditProfileDlg']); ManageProfileOverlay.showManageDialog(); }; $('profiles-delete').onclick = function(event) { var selectedProfile = self.getSelectedProfileItem_(); - if (selectedProfile) + if (selectedProfile) { + chrome.send('metricsHandler:recordAction', + ['Options_ShowDeleteProfileDlg']); ManageProfileOverlay.showDeleteDialog(selectedProfile); + } }; if (loadTimeData.getBoolean('profileIsSupervised')) { $('profiles-create').disabled = true;
diff --git a/chrome/browser/resources/options/chromeos/internet_detail.js b/chrome/browser/resources/options/chromeos/internet_detail.js index a077fbd..449ee372 100644 --- a/chrome/browser/resources/options/chromeos/internet_detail.js +++ b/chrome/browser/resources/options/chromeos/internet_detail.js
@@ -235,6 +235,10 @@ initializePage: function() { Page.prototype.initializePage.call(this); this.initializePageContents_(); + + chrome.networkingPrivate.onNetworksChanged.addListener( + this.onNetworksChanged_.bind(this)); + this.showNetworkDetails_(); }, @@ -252,6 +256,22 @@ }, /** + * networkingPrivate callback when networks change. + * @param {Array<string>} changes List of GUIDs whose properties have + * changed. + * @private + */ + onNetworksChanged_: function(changes) { + if (!this.onc_) + return; + var guid = this.onc_.guid(); + if (changes.indexOf(guid) != -1) { + chrome.networkingPrivate.getManagedProperties( + guid, DetailsInternetPage.updateConnectionData); + } + }, + + /** * Initializes the contents of the page. */ initializePageContents_: function() { @@ -1011,9 +1031,7 @@ * Event Listener for the cellular-apn-cancel button. * @private */ - cancelApn_: function() { - this.initializeApnList_(); - }, + cancelApn_: function() { this.initializeApnList_(); }, /** * Event Listener for the select-apn button. @@ -1421,9 +1439,6 @@ detailsPage.updateConnectionButtonVisibilty_(); detailsPage.updateDetails_(); - // Inform chrome which network to pass events for in InternetOptionsHandler. - chrome.send('setNetworkGuid', [detailsPage.onc_.guid()]); - // TODO(stevenjb): Some of the setup below should be moved to // updateDetails_() so that updates are reflected in the UI.
diff --git a/chrome/browser/resources/options/chromeos/network_list.js b/chrome/browser/resources/options/chromeos/network_list.js index 8a1b99c..2dd366a 100644 --- a/chrome/browser/resources/options/chromeos/network_list.js +++ b/chrome/browser/resources/options/chromeos/network_list.js
@@ -3,19 +3,23 @@ // found in the LICENSE file. /** - * This partially describes the network list entries passed to - * refreshNetworkData. The contents of those lists actually match - * chrome.networkingPrivate.NetworkStateProperties with the addition of the - * policyManaged property. TODO(stevenjb): Use networkingPrivate.getNetworks. + * Partial definition of the result of networkingPrivate.getProperties()). * @typedef {{ * ConnectionState: string, + * Cellular: { + * Family: ?string, + * SIMPresent: ?boolean, + * SIMLockStatus: { LockType: ?string }, + * SupportNetworkScan: ?boolean + * }, * GUID: string, - * Type: string, - * policyManaged: boolean + * Name: string, + * Source: string, + * Type: string * }} - * @see chrome/browser/ui/webui/options/chromeos/internet_options_handler.cc + * @see extensions/common/api/networking_private.idl */ -var NetworkInfo; +var NetworkProperties; cr.define('options.network', function() { var ArrayDataModel = cr.ui.ArrayDataModel; @@ -68,42 +72,35 @@ * @type {string|undefined} * @private */ - var cellularState_ = undefined; + var cellularDeviceState_ = undefined; /** - * Indicates if cellular device supports network scanning. - * @type {boolean} + * The active cellular network or null if none. + * @type {?NetworkProperties} * @private */ - var cellularSupportsScan_ = false; + var cellularNetwork_ = null; /** - * Indicates the current SIM lock type of the cellular device. - * @type {string} + * The active ethernet network or null if none. + * @type {?NetworkProperties} * @private */ - var cellularSimLockType_ = ''; - - /** - * Indicates whether the SIM card is absent on the cellular device. - * @type {boolean} - * @private - */ - var cellularSimAbsent_ = false; + var ethernetNetwork_ = null; /** * The state of the WiFi device or undefined if not available. * @type {string|undefined} * @private */ - var wifiState_ = undefined; + var wifiDeviceState_ = undefined; /** * The state of the WiMAX device or undefined if not available. * @type {string|undefined} * @private */ - var wimaxState_ = undefined; + var wimaxDeviceState_ = undefined; /** * Indicates if mobile data roaming is enabled. @@ -113,36 +110,8 @@ var enableDataRoaming_ = false; /** - * List of wired networks. - * @type {Array<!NetworkInfo>} - * @private - */ - var wiredList_ = []; - - /** - * List of WiFi, Cellular, and WiMAX networks. - * @type {Array<!NetworkInfo>} - * @private - */ - var wirelessList_ = []; - - /** - * List of VPN networks. - * @type {Array<!NetworkInfo>} - * @private - */ - var vpnList_ = []; - - /** - * List of remembered (favorite) networks. - * @type {Array<!NetworkInfo>} - * @private - */ - var rememberedList_ = []; - - /** * Returns the display name for 'network'. - * @param {Object} data The network data dictionary. + * @param {NetworkProperties} data The network data dictionary. */ function getNetworkName(data) { if (data.Type == 'Ethernet') @@ -236,7 +205,7 @@ /** * Sets the icon based on a network state object. - * @param {!Object} data Object containing network state data. + * @param {!NetworkProperties} data Network state properties. */ set iconData(data) { if (!isNetworkType(data.Type)) @@ -459,8 +428,8 @@ /** * Creates a control for selecting or configuring a network connection based * on the type of connection (e.g. wifi versus vpn). - * @param {{key: string, networkList: Array<NetworkInfo>}} data Description - * of the network. + * @param {{key: string, networkList: Array<!NetworkProperties>}} data + * An object containing the network type (key) and an array of networks. * @constructor * @extends {NetworkMenuItem} */ @@ -471,13 +440,59 @@ return el; } + /** + * Returns true if |source| is a policy managed source. + * @param {string} source The ONC source of a network. + * @return {boolean} Whether |source| is a managed source. + */ + function isManaged(source) { + return (source == 'DevicePolicy' || source == 'UserPolicy'); + } + + /** + * Returns true if |network| is visible. + * @param {!chrome.networkingPrivate.NetworkStateProperties} network The + * network state properties. + * @return {boolean} Whether |network| is visible. + */ + function networkIsVisible(network) { + if (network.Type == 'WiFi') + return !!(network.WiFi && (network.WiFi.SignalStrength > 0)); + if (network.Type == 'WiMAX') + return !!(network.WiMAX && (network.WiMAX.SignalStrength > 0)); + // Other network types are always considered 'visible'. + return true; + } + + /** + * Returns true if |cellular| is a GSM network with no sim present. + * @param {?NetworkProperties} cellular The network state properties. + * @return {boolean} Whether |network| is missing a SIM card. + */ + function isCellularSimAbsent(cellular) { + if (!cellular || !cellular.Cellular) + return false; + return cellular.Cellular.Family == 'GSM' && !cellular.Cellular.SIMPresent; + } + + /** + * Returns true if |cellular| has a locked SIM card. + * @param {?NetworkProperties} cellular The network state properties. + * @return {boolean} Whether |network| has a locked SIM card. + */ + function isCellularSimLocked(cellular) { + if (!cellular || !cellular.Cellular) + return false; + var simLockStatus = cellular.Cellular.SIMLockStatus; + return !!(simLockStatus && simLockStatus.LockType); + } + NetworkSelectorItem.prototype = { __proto__: NetworkMenuItem.prototype, /** @override */ decorate: function() { // TODO(kevers): Generalize method of setting default label. - var policyManaged = false; this.subtitle = loadTimeData.getString('OncConnectionStateNotConnected'); var list = this.data_.networkList; var candidateData = null; @@ -487,7 +502,6 @@ networkDetails.ConnectionState == 'Connected') { this.subtitle = getNetworkName(networkDetails); this.setSubtitleDirection('ltr'); - policyManaged = networkDetails.policyManaged; candidateData = networkDetails; // Only break when we see a connecting network as it is possible to // have a connected network and a connecting network at the same @@ -503,7 +517,7 @@ this.showSelector(); - if (policyManaged) + if (candidateData && isManaged(candidateData.Source)) this.showManagedNetworkIndicator(); if (activeMenu_ == this.getMenuName()) { @@ -536,7 +550,9 @@ data: {} }); } else if (this.data_.key == 'Cellular') { - if (cellularState_ == 'Enabled' && cellularSupportsScan_) { + if (cellularDeviceState_ == 'Enabled' && + cellularNetwork_ && cellularNetwork_.Cellular && + cellularNetwork_.Cellular.SupportNetworkScan) { addendum.push({ label: loadTimeData.getString('otherCellularNetworks'), command: createAddNonVPNConnectionCallback_('Cellular'), @@ -737,7 +753,7 @@ /** * Adds a menu item for showing network details. * @param {!Element} parent The parent element. - * @param {Object} data Description of the network. + * @param {NetworkProperties} data Description of the network. * @private */ createNetworkOptionsCallback_: function(parent, data) { @@ -745,7 +761,7 @@ data, getNetworkName(data), showDetails.bind(null, data.GUID)); - if (data.policyManaged) + if (isManaged(data.Source)) menuItem.appendChild(new ManagedNetworkIndicator()); if (data.ConnectionState == 'Connected' || data.ConnectionState == 'Connecting') { @@ -785,7 +801,7 @@ this.iconData = this.data.iconData; else if (this.data.iconType) this.iconType = this.data.iconType; - if (this.data.policyManaged) + if (isManaged(this.data.Source)) this.showManagedNetworkIndicator(); }, }; @@ -793,7 +809,7 @@ /** * Adds a command to a menu for modifying network settings. * @param {!Element} menu Parent menu. - * @param {Object} data Description of the network. + * @param {?NetworkProperties} data Description of the network. * @param {!string} label Display name for the menu item. * @param {!Function} command Callback function. * @return {!Element} The created menu item. @@ -876,10 +892,30 @@ }); this.endBatchUpdates(); + this.onNetworkListChanged_(); // Trigger an initial network update + + chrome.networkingPrivate.onNetworkListChanged.addListener( + this.onNetworkListChanged_.bind(this)); + + chrome.networkingPrivate.requestNetworkScan(); + options.VPNProviders.addObserver(this.onVPNProvidersChanged_.bind(this)); }, /** + * networkingPrivate event called when the network list has changed. + */ + onNetworkListChanged_: function() { + var networkList = this; + chrome.networkingPrivate.getDeviceStates(function(deviceStates) { + var filter = { networkType: 'All' }; + chrome.networkingPrivate.getNetworks(filter, function(networkStates) { + networkList.updateNetworkStates(deviceStates, networkStates); + }); + }); + }, + + /** * Called when the list of VPN providers changes. Refreshes the contents of * menus that list VPN providers. * @private @@ -995,7 +1031,7 @@ /** * Updates a network control. - * @param {Object<string,string>} data Description of the entry. + * @param {Object} data Description of the entry. */ update: function(data) { this.startBatchUpdates(); @@ -1043,8 +1079,8 @@ createItem: function(entry) { if (entry.networkList) return new NetworkSelectorItem( - /** @type {{key: string, networkList: Array<NetworkInfo>}} */( - entry)); + /** @type {{key: string, networkList: Array<!NetworkProperties>}} */ + (entry)); if (entry.command) return new NetworkButtonItem( /** @type {{key: string, subtitle: string, command: Function}} */( @@ -1076,105 +1112,122 @@ entry.iconType = active ? 'control-active' : 'control-inactive'; this.update(entry); } - } - }; + }, - /** - * Chrome callback for updating network controls. - * @param {{cellularSimAbsent: boolean, - * cellularSimLockType: string, - * cellularSupportsScan: boolean, - * rememberedList: Array<NetworkInfo>, - * vpnList: Array<NetworkInfo>, - * wiredList: Array<NetworkInfo>, - * wirelessList: Array<NetworkInfo>}} data Description of available - * network devices and their corresponding state. - */ - NetworkList.refreshNetworkData = function(data) { - cellularSupportsScan_ = data.cellularSupportsScan; - cellularSimAbsent_ = data.cellularSimAbsent; - cellularSimLockType_ = data.cellularSimLockType; - wiredList_ = data.wiredList; - wirelessList_ = data.wirelessList; - vpnList_ = data.vpnList; - rememberedList_ = data.rememberedList; + /** + * Updates the state of network devices and services. + * @param {!Array<{State: string, Type: string}>} deviceStates The result + * from networkingPrivate.getDeviceStates. + * @param {!Array<!chrome.networkingPrivate.NetworkStateProperties>} + * networkStates The result from networkingPrivate.getNetworks. + */ + updateNetworkStates: function(deviceStates, networkStates) { + // Update device states. + cellularDeviceState_ = undefined; + wifiDeviceState_ = undefined; + wimaxDeviceState_ = undefined; + for (var i = 0; i < deviceStates.length; ++i) { + var device = deviceStates[i]; + var type = device.Type; + var state = device.State; + if (type == 'Cellular') + cellularDeviceState_ = cellularDeviceState_ || state; + else if (type == 'WiFi') + wifiDeviceState_ = wifiDeviceState_ || state; + else if (type == 'WiMAX') + wimaxDeviceState_ = wimaxDeviceState_ || state; + } - // Request device states. - chrome.networkingPrivate.getDeviceStates( - NetworkList.onGetDeviceStates.bind(this)); - }; + // Update active network states. + cellularNetwork_ = null; + ethernetNetwork_ = null; + for (var i = 0; i < networkStates.length; i++) { + // Note: This cast is valid since + // networkingPrivate.NetworkStateProperties is a subset of + // NetworkProperties and all missing properties are optional. + var entry = /** @type {NetworkProperties} */ (networkStates[i]); + switch (entry.Type) { + case 'Cellular': + cellularNetwork_ = cellularNetwork_ || entry; + break; + case 'Ethernet': + ethernetNetwork_ = ethernetNetwork_ || entry; + break; + } + if (cellularNetwork_ && ethernetNetwork_) + break; + } - /** - * Callback from getDeviceStates. Updates network controls. - * @param {Array<{State: string, Type: string}>} deviceStates The result - * from getDeviceStates. - */ - NetworkList.onGetDeviceStates = function(deviceStates) { - var networkList = $('network-list'); - networkList.startBatchUpdates(); + if (cellularNetwork_ && cellularNetwork_.GUID) { + // Get the complete set of cellular properties which includes SIM and + // Scan properties. + var networkList = this; + chrome.networkingPrivate.getProperties( + cellularNetwork_.GUID, function(cellular) { + cellularNetwork_ = /** @type {NetworkProperties} */ (cellular); + networkList.updateControls(networkStates); + }); + } else { + this.updateControls(networkStates); + } + }, - cellularState_ = undefined; - wifiState_ = undefined; - wimaxState_ = undefined; - for (var i = 0; i < deviceStates.length; ++i) { - var device = deviceStates[i]; - var type = device.Type; - var state = device.State; - if (type == 'Cellular') - cellularState_ = cellularState_ || state; - else if (type == 'WiFi') - wifiState_ = wifiState_ || state; - else if (type == 'WiMAX') - wimaxState_ = wimaxState_ || state; - } + /** + * Updates network controls. + * @param {!Array<!chrome.networkingPrivate.NetworkStateProperties>} + * networkStates The result from networkingPrivate.getNetworks. + */ + updateControls: function(networkStates) { + this.startBatchUpdates(); - // Only show Ethernet control if connected. - var ethernetConnection = getConnection_(wiredList_); - if (ethernetConnection) { - var type = String('Ethernet'); - var ethernetOptions = showDetails.bind(null, ethernetConnection.GUID); - networkList.update( + // Only show Ethernet control if connected. + if (ethernetNetwork_ && ethernetNetwork_.ConnectionState == 'Connected') { + var ethernetOptions = showDetails.bind(null, ethernetNetwork_.GUID); + this.update( { key: 'Ethernet', subtitle: loadTimeData.getString('OncConnectionStateConnected'), - iconData: ethernetConnection, + iconData: ethernetNetwork_, command: ethernetOptions, - policyManaged: ethernetConnection.policyManaged } - ); - } else { - networkList.deleteItem('Ethernet'); - } + Source: ethernetNetwork_.Source } + ); + } else { + this.deleteItem('Ethernet'); + } - if (wifiState_ == 'Enabled') - loadData_('WiFi', wirelessList_, rememberedList_); - else - addEnableNetworkButton_('WiFi'); - - // Only show cellular control if available. - if (cellularState_) { - if (cellularState_ == 'Enabled') - loadData_('Cellular', wirelessList_, rememberedList_); + if (wifiDeviceState_ == 'Enabled') + loadData_('WiFi', networkStates); else - addEnableNetworkButton_('Cellular'); - } else { - networkList.deleteItem('Cellular'); - } + addEnableNetworkButton_('WiFi'); - // Only show wimax control if available. Uses cellular icons. - if (wimaxState_) { - if (wimaxState_ == 'Enabled') - loadData_('WiMAX', wirelessList_, rememberedList_); - else - addEnableNetworkButton_('WiMAX'); - } else { - networkList.deleteItem('WiMAX'); - } + // Only show cellular control if available. + if (cellularDeviceState_) { + if (cellularDeviceState_ == 'Enabled' && + !isCellularSimLocked(cellularNetwork_) && + !isCellularSimAbsent(cellularNetwork_)) { + loadData_('Cellular', networkStates); + } else { + addEnableNetworkButton_('Cellular'); + } + } else { + this.deleteItem('Cellular'); + } - // Only show VPN control if there is at least one VPN configured. - if (vpnList_.length > 0) - loadData_('VPN', vpnList_, rememberedList_); - else - networkList.deleteItem('VPN'); - networkList.endBatchUpdates(); + // Only show wimax control if available. Uses cellular icons. + if (wimaxDeviceState_) { + if (wimaxDeviceState_ == 'Enabled') + loadData_('WiMAX', networkStates); + else + addEnableNetworkButton_('WiMAX'); + } else { + this.deleteItem('WiMAX'); + } + + // Only show VPN control if there is at least one VPN configured. + if (loadData_('VPN', networkStates) == 0) + this.deleteItem('VPN'); + + this.endBatchUpdates(); + } }; /** @@ -1188,10 +1241,10 @@ if (type == 'WiFi') sendChromeMetricsAction('Options_NetworkWifiToggle'); if (type == 'Cellular') { - if (cellularSimLockType_) { + if (isCellularSimLocked(cellularNetwork_)) { chrome.send('simOperation', ['unlock']); return; - } else if (cellularState_ == 'Enabled' && cellularSimAbsent_) { + } else if (isCellularSimAbsent(cellularNetwork_)) { chrome.send('simOperation', ['configure']); return; } @@ -1264,26 +1317,34 @@ * Updates the list of available networks and their status, filtered by * network type. * @param {string} type The type of network. - * @param {Array} available The list of available networks and their status. - * @param {Array} remembered The list of remmebered networks. + * @param {Array<!chrome.networkingPrivate.NetworkStateProperties>} networks + * The list of network objects. + * @return {number} The number of visible networks matching |type|. */ - function loadData_(type, available, remembered) { - var data = {key: type}; + function loadData_(type, networks) { + var res = 0; var availableNetworks = []; - for (var i = 0; i < available.length; i++) { - if (available[i].Type == type) - availableNetworks.push(available[i]); - } - data.networkList = availableNetworks; - if (remembered) { - var rememberedNetworks = []; - for (var i = 0; i < remembered.length; i++) { - if (remembered[i].Type == type) - rememberedNetworks.push(remembered[i]); + var rememberedNetworks = []; + for (var i = 0; i < networks.length; i++) { + var network = networks[i]; + if (network.Type != type) + continue; + if (networkIsVisible(network)) { + availableNetworks.push(network); + ++res; } - data.rememberedNetworks = rememberedNetworks; + if ((type == 'WiFi' || type == 'VPN') && network.Source && + network.Source != 'None') { + rememberedNetworks.push(network); + } } + var data = { + key: type, + networkList: availableNetworks, + rememberedNetworks: rememberedNetworks + }; $('network-list').update(data); + return res; } /** @@ -1301,24 +1362,6 @@ } /** - * Fetches the active connection. - * @param {Array<Object>} networkList List of networks. - * @return {Object} - * @private - */ - function getConnection_(networkList) { - if (!networkList) - return null; - for (var i = 0; i < networkList.length; i++) { - var entry = networkList[i]; - if (entry.ConnectionState == 'Connected' || - entry.ConnectionState == 'Connecting') - return entry; - } - return null; - } - - /** * Creates a callback function that adds a new connection of the given type. * This method may be used for all network types except VPN. * @param {string} type An ONC network type
diff --git a/chrome/browser/resources/options/chromeos/preferred_networks.js b/chrome/browser/resources/options/chromeos/preferred_networks.js index fb20b057..4f5a152b 100644 --- a/chrome/browser/resources/options/chromeos/preferred_networks.js +++ b/chrome/browser/resources/options/chromeos/preferred_networks.js
@@ -5,7 +5,7 @@ cr.exportPath('options'); /** - * @typedef {{Name: string, Type: string, GUID: string}} + * @typedef {{GUID: string, Name: string, Source: string, Type: string}} */ options.PreferredNetwork; @@ -83,8 +83,10 @@ DeletableItem.prototype.decorate.call(this); var label = this.ownerDocument.createElement('div'); label.textContent = this.data.Name; - if (this.data.policyManaged) + if (this.data.Source == 'DevicePolicy' || + this.data.Source == 'UserPolicy') { this.deletable = false; + } this.contentElement.appendChild(label); } };
diff --git a/chrome/browser/resources/pdf/browser_api.js b/chrome/browser/resources/pdf/browser_api.js new file mode 100644 index 0000000..52ce4c6 --- /dev/null +++ b/chrome/browser/resources/pdf/browser_api.js
@@ -0,0 +1,159 @@ +// 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. + +'use strict'; + +let defaultZoomPromise = (function() { + if (!chrome.tabs) + return Promise.resolve(1); + + return new Promise(function(resolve, reject) { + chrome.tabs.getZoomSettings(function(zoomSettings) { + resolve(zoomSettings.defaultZoomFactor); + }.bind(this)); + }.bind(this)); +})(); + +/** + * A class providing an interface to the browser. + */ +class BrowserApi { + /** + * @constructor + * @param {!Object} streamInfo The stream object which points to the data + * contained in the PDF. + * @param {number} defaultZoom The default browser zoom. + * @param {boolean} manageZoom Whether to manage zoom. + */ + constructor(streamInfo, defaultZoom, manageZoom) { + this.streamInfo_ = streamInfo; + this.defaultZoom_ = defaultZoom; + this.manageZoom_ = manageZoom; + } + + /** + * Returns a promise to a BrowserApi. + * @param {!Promise.<Object>} streamInfoPromise A promise that will resolve + * the stream object pointing to the data contained in the PDF. + * @param {boolean} manageZoom Whether to manage zoom. + */ + static create(streamInfoPromise, manageZoom) { + return Promise.all([streamInfoPromise, defaultZoomPromise]).then( + function(results) { + return new BrowserApi(results[0], results[1], manageZoom); + }); + } + + /** + * Returns the stream info pointing to the data contained in the PDF. + * @return {Object} The stream info object. + */ + getStreamInfo() { + return this.streamInfo_; + } + + /** + * Aborts the stream. + */ + abortStream() { + if (chrome.mimeHandlerPrivate) + chrome.mimeHandlerPrivate.abortStream(); + } + + /** + * Sets the browser zoom. + * @param {number} zoom The zoom factor to send to the browser. + * @return {Promise} A promise that will be resolved when the browser zoom + * has been updated. + */ + setZoom(zoom) { + if (!this.manageZoom_) + return Promise.resolve(); + return new Promise(function(resolve, reject) { + chrome.tabs.setZoom(this.streamInfo_.tabId, zoom, resolve); + }.bind(this)); + } + + /** + * Returns the default browser zoom factor. + * @return {number} The default browser zoom factor. + */ + getDefaultZoom() { + return this.defaultZoom_; + } + + /** + * Adds an event listener to be notified when the browser zoom changes. + * @param {function} listener The listener to be called with the new zoom + * factor. + */ + addZoomEventListener(listener) { + if (!this.manageZoom_) + return; + + chrome.tabs.onZoomChange.addListener(function(zoomChangeInfo) { + if (zoomChangeInfo.tabId != this.streamInfo_.tabId) + return; + listener(zoomChangeInfo.newZoomFactor); + }.bind(this)); + } +}; + +/** + * Creates a BrowserApi for an extension running as a mime handler. + * @return {Promise.<BrowserApi>} A promise to a BrowserApi instance constructed + * using the mimeHandlerPrivate API. + */ +function createBrowserApiForMimeHandlerView() { + return new Promise(function(resolve, reject) { + chrome.mimeHandlerPrivate.getStreamInfo(resolve); + }).then(function(streamInfo) { + if (streamInfo.embedded || streamInfo.tabId == -1) + return BrowserApi.create(streamInfo, false); + + return BrowserApi.create(new Promise(function(resolve, reject) { + chrome.tabs.setZoomSettings( + streamInfo.tabId, {mode: 'manual', scope: 'per-tab'}, resolve); + }).then(function() { + return streamInfo; + }), true); + }); +} + +/** + * Creates a BrowserApi instance for an extension not running as a mime handler. + * @return {Promise.<BrowserApi>} A promise to a BrowserApi instance constructed + * from the URL. + */ +function createBrowserApiForStandaloneExtension() { + let url = window.location.search.substring(1); + let streamInfo = { + streamUrl: url, + originalUrl: url, + responseHeaders: {}, + embedded: window.parent != window, + tabId: -1, + }; + if (!chrome.tabs) + return BrowserApi.create(streamInfo, false); + + return BrowserApi.create(new Promise(function(resolve, reject) { + chrome.tabs.getCurrent(function(tab) { + streamInfo.tabId = tab.id; + resolve(streamInfo); + }); + }), false); +} + +/** + * Returns a promise that will resolve to a BrowserApi instance. + * @return {Promise.<BrowserApi>} A promise to a BrowserApi instance for the + * current environment. + */ +function createBrowserApi() { + if (window.location.search) + return createBrowserApiForStandaloneExtension(); + + return createBrowserApiForMimeHandlerView(); +}
diff --git a/chrome/browser/resources/pdf/index-material.html b/chrome/browser/resources/pdf/index-material.html index ec0e9d0..b71ef4c 100644 --- a/chrome/browser/resources/pdf/index-material.html +++ b/chrome/browser/resources/pdf/index-material.html
@@ -38,6 +38,7 @@ <script src="zoom_manager.js"></script> <script src="pdf_scripting_api.js"></script> <script src="chrome://resources/js/util.js"></script> +<script src="browser_api.js"></script> <script src="pdf.js"></script> <script src="main.js"></script> </html>
diff --git a/chrome/browser/resources/pdf/index.html b/chrome/browser/resources/pdf/index.html index 7062cec..2ecf2c8 100644 --- a/chrome/browser/resources/pdf/index.html +++ b/chrome/browser/resources/pdf/index.html
@@ -55,6 +55,7 @@ <script src="zoom_manager.js"></script> <script src="pdf_scripting_api.js"></script> <script src="chrome://resources/js/util.js"></script> +<script src="browser_api.js"></script> <script src="pdf.js"></script> <script src="main.js"></script> </html>
diff --git a/chrome/browser/resources/pdf/main.js b/chrome/browser/resources/pdf/main.js index 07fa2e0..e6938bc 100644 --- a/chrome/browser/resources/pdf/main.js +++ b/chrome/browser/resources/pdf/main.js
@@ -29,56 +29,26 @@ /** * Initialize the global PDFViewer and pass any outstanding messages to it. - * @param {Object} streamDetails The stream object which points to the data - * contained in the PDF. + * @param {Object} browserApi An object providing an API to the browser. */ - function initViewer(streamDetails) { + function initViewer(browserApi) { // PDFViewer will handle any messages after it is created. window.removeEventListener('message', handleScriptingMessage, false); - viewer = new PDFViewer(streamDetails); + viewer = new PDFViewer(browserApi); while (pendingMessages.length > 0) viewer.handleScriptingMessage(pendingMessages.shift()); } - function generateStreamDetailsAndInitViewer() { - var url = window.location.search.substring(1); - var streamDetails = { - streamUrl: url, - originalUrl: url, - responseHeaders: '', - embedded: window.parent != window, - tabId: -1 - }; - if (!chrome.tabs) { - initViewer(streamDetails); - return; - } - chrome.tabs.getCurrent(function(tab) { - streamDetails.tabId = tab.id; - initViewer(streamDetails); - }); - } - /** - * Entrypoint for starting the PDF viewer. This function obtains the details - * of the PDF 'stream' (the data that points to the PDF) and constructs a - * PDFViewer object with it. + * Entrypoint for starting the PDF viewer. This function obtains the browser + * API for the PDF and constructs a PDFViewer object with it. */ function main() { // Set up an event listener to catch scripting messages which are sent prior // to the PDFViewer being created. window.addEventListener('message', handleScriptingMessage, false); - if (window.location.search) { - generateStreamDetailsAndInitViewer(); - return; - } - - // If the viewer is started from the browser plugin, getStreamInfo will - // return the details of the stream. - chrome.mimeHandlerPrivate.getStreamInfo(function(streamDetails) { - initViewer(streamDetails); - }); + createBrowserApi().then(initViewer); }; main();
diff --git a/chrome/browser/resources/pdf/pdf.js b/chrome/browser/resources/pdf/pdf.js index 47baecd..6e94d56 100644 --- a/chrome/browser/resources/pdf/pdf.js +++ b/chrome/browser/resources/pdf/pdf.js
@@ -61,18 +61,17 @@ * Creates a new PDFViewer. There should only be one of these objects per * document. * @constructor - * @param {Object} streamDetails The stream object which points to the data - * contained in the PDF. + * @param {!BrowserApi} browserApi An object providing an API to the browser. */ -function PDFViewer(streamDetails) { - this.streamDetails_ = streamDetails; +function PDFViewer(browserApi) { + this.browserApi_ = browserApi; this.loaded_ = false; this.parentWindow_ = null; this.delayedScriptingMessages_ = []; - this.isPrintPreview_ = - this.streamDetails_.originalUrl.indexOf('chrome://print') == 0; + this.isPrintPreview_ = this.browserApi_.getStreamInfo().originalUrl.indexOf( + 'chrome://print') == 0; this.isMaterial_ = location.pathname.substring(1) === 'index-material.html'; // The sizer element is placed behind the plugin element to cause scrollbars @@ -93,7 +92,8 @@ this.viewportChanged_.bind(this), this.beforeZoom_.bind(this), this.afterZoom_.bind(this), - getScrollbarWidth()); + getScrollbarWidth(), + this.browserApi_.getDefaultZoom()); // Create the plugin object dynamically so we can set its src. The plugin // element is sized to fill the entire window and is set to be fixed @@ -113,20 +113,23 @@ window.addEventListener('message', this.handleScriptingMessage.bind(this), false); - document.title = getFilenameFromURL(this.streamDetails_.originalUrl); - this.plugin_.setAttribute('src', this.streamDetails_.originalUrl); - this.plugin_.setAttribute('stream-url', this.streamDetails_.streamUrl); + document.title = + getFilenameFromURL(this.browserApi_.getStreamInfo().originalUrl); + this.plugin_.setAttribute('src', + this.browserApi_.getStreamInfo().originalUrl); + this.plugin_.setAttribute('stream-url', + this.browserApi_.getStreamInfo().streamUrl); var headers = ''; - for (var header in this.streamDetails_.responseHeaders) { + for (var header in this.browserApi_.getStreamInfo().responseHeaders) { headers += header + ': ' + - this.streamDetails_.responseHeaders[header] + '\n'; + this.browserApi_.getStreamInfo().responseHeaders[header] + '\n'; } this.plugin_.setAttribute('headers', headers); if (this.isMaterial_) this.plugin_.setAttribute('is-material', ''); - if (!this.streamDetails_.embedded) + if (!this.browserApi_.getStreamInfo().embedded) this.plugin_.setAttribute('full-frame', ''); document.body.appendChild(this.plugin_); @@ -176,19 +179,12 @@ [this.bookmarksPane_]); } - // Set up the zoom API. - if (this.shouldManageZoom_()) { - chrome.tabs.setZoomSettings(this.streamDetails_.tabId, - {mode: 'manual', scope: 'per-tab'}, function() { - this.zoomManager_ = - new ZoomManager(this.viewport_, this.setZoom_.bind(this)); - chrome.tabs.onZoomChange.addListener(function(zoomChangeInfo) { - if (zoomChangeInfo.tabId != this.streamDetails_.tabId) - return; - this.zoomManager_.onBrowserZoomChange(zoomChangeInfo.newZoomFactor); - }.bind(this)); - }.bind(this)); - } + // Set up the ZoomManager. + this.zoomManager_ = new ZoomManager( + this.viewport_, this.browserApi_.setZoom.bind(this.browserApi_), + this.browserApi_.getDefaultZoom()); + this.browserApi_.addZoomEventListener( + this.zoomManager_.onBrowserZoomChange.bind(this.zoomManager_)); // Setup the keyboard event listener. document.onkeydown = this.handleKeyEvent_.bind(this); @@ -196,7 +192,7 @@ // Parse open pdf parameters. this.paramsParser_ = new OpenPDFParamsParser(this.getNamedDestination_.bind(this)); - this.navigator_ = new Navigator(this.streamDetails_.originalUrl, + this.navigator_ = new Navigator(this.browserApi_.getStreamInfo().originalUrl, this.viewport_, this.paramsParser_, onNavigateInCurrentTab, onNavigateInNewTab); this.viewportScroller_ = @@ -425,7 +421,8 @@ if (this.lastViewportPosition_) this.viewport_.position = this.lastViewportPosition_; this.paramsParser_.getViewportFromUrlParams( - this.streamDetails_.originalUrl, this.handleURLParams_.bind(this)); + this.browserApi_.getStreamInfo().originalUrl, + this.handleURLParams_.bind(this)); this.loaded_ = true; this.sendScriptingMessage_({ type: 'documentLoaded' @@ -571,14 +568,7 @@ xOffset: position.x, yOffset: position.y }); - if (this.zoomManager_) - this.zoomManager_.onPdfZoomChange(); - }, - - setZoom_: function(zoom) { - return new Promise(function(resolve, reject) { - chrome.tabs.setZoom(this.streamDetails_.tabId, zoom, resolve); - }.bind(this)); + this.zoomManager_.onPdfZoomChange(); }, /** @@ -749,16 +739,6 @@ this.parentWindow_.postMessage(message, '*'); }, - /** - * @private - * Return whether this PDFViewer should manage zoom for its containing page. - * @return {boolean} Whether this PDFViewer should manage zoom for its - * containing page. - */ - shouldManageZoom_: function() { - return !!(chrome.tabs && !this.streamDetails_.embedded && - this.streamDetails_.tabId != -1); - }, /** * @type {Viewport} the viewport of the PDF viewer.
diff --git a/chrome/browser/resources/pdf/viewport.js b/chrome/browser/resources/pdf/viewport.js index 20c8460..47c2507 100644 --- a/chrome/browser/resources/pdf/viewport.js +++ b/chrome/browser/resources/pdf/viewport.js
@@ -24,13 +24,15 @@ * @param {Function} beforeZoomCallback is run before a change in zoom * @param {Function} afterZoomCallback is run after a change in zoom * @param {number} scrollbarWidth the width of scrollbars on the page + * @param {number} defaultZoom The default zoom level. */ function Viewport(window, sizer, viewportChangedCallback, beforeZoomCallback, afterZoomCallback, - scrollbarWidth) { + scrollbarWidth, + defaultZoom) { this.window_ = window; this.sizer_ = sizer; this.viewportChangedCallback_ = viewportChangedCallback; @@ -42,6 +44,7 @@ this.pageDimensions_ = []; this.scrollbarWidth_ = scrollbarWidth; this.fittingType_ = Viewport.FittingType.NONE; + this.defaultZoom_ = defaultZoom; window.addEventListener('scroll', this.updateViewport_.bind(this)); window.addEventListener('resize', this.resize_.bind(this)); @@ -498,10 +501,9 @@ this.documentDimensions_ = documentDimensions; this.pageDimensions_ = this.documentDimensions_.pageDimensions; if (initialDimensions) { - this.setZoomInternal_(this.computeFittingZoom_(this.documentDimensions_, - true)); - if (this.zoom_ > 1) - this.setZoomInternal_(1); + this.setZoomInternal_( + Math.min(this.defaultZoom_, + this.computeFittingZoom_(this.documentDimensions_, true))); this.window_.scrollTo(0, 0); } this.contentSizeChanged_();
diff --git a/chrome/browser/resources/pdf/zoom_manager.js b/chrome/browser/resources/pdf/zoom_manager.js index 664c88f..14c57a06 100644 --- a/chrome/browser/resources/pdf/zoom_manager.js +++ b/chrome/browser/resources/pdf/zoom_manager.js
@@ -13,13 +13,13 @@ * @param {!Viewport} viewport A Viewport for which to manage zoom. * @param {Function} setBrowserZoomFunction A function that sets the browser * zoom to the provided value. + * @param {number} defaultZoom The default browser zoom level. */ - constructor(viewport, setBrowserZoomFunction) { + constructor(viewport, setBrowserZoomFunction, defaultZoom) { this.viewport_ = viewport; this.setBrowserZoomFunction_ = setBrowserZoomFunction; - this.browserZoom_ = 1; + this.browserZoom_ = defaultZoom; this.changingBrowserZoom_ = null; - this.onPdfZoomChange(); } /**
diff --git a/chrome/browser/resources/security_warnings/interstitial_v2.css b/chrome/browser/resources/security_warnings/interstitial_v2.css index 81087bc..7987f80 100644 --- a/chrome/browser/resources/security_warnings/interstitial_v2.css +++ b/chrome/browser/resources/security_warnings/interstitial_v2.css
@@ -338,10 +338,10 @@ (min-height: 401px) and (orientation:portrait), (min-width: 421px) and (max-width: 736px) and (min-height: 240px) and (max-height: 420px) and (orientation:landscape) { - body .nav-wrapper { background: #f7f7f7; bottom: 0; + box-shadow: 0 -22px 40px rgb(247, 247, 247); left: 0; margin: 0; max-width: 736px; @@ -351,11 +351,17 @@ body.safe-browsing .nav-wrapper { background: rgb(206, 52, 38); + box-shadow: 0 -22px 40px rgb(206, 52, 38); } .interstitial-wrapper { max-width: 736px; } + + #details, + #main-content { + padding-bottom: 30px; + } } @media (max-width: 420px) and (orientation: portrait), @@ -637,6 +643,7 @@ .extended-reporting-has-checkbox #main-content { flex: 1 1 auto; order: 0; + padding-bottom: 0; } .extended-reporting-has-checkbox #extended-reporting-opt-in { @@ -653,3 +660,10 @@ width: 100%; } } + +@media (max-width: 239px) and (orientation: portrait) { + .nav-wrapper { + padding-left: 0; + padding-right: 0; + } +}
diff --git a/chrome/browser/safe_browsing/incident_reporting/module_integrity_unittest_util_win.cc b/chrome/browser/safe_browsing/incident_reporting/module_integrity_unittest_util_win.cc index 1293bd4..a616608 100644 --- a/chrome/browser/safe_browsing/incident_reporting/module_integrity_unittest_util_win.cc +++ b/chrome/browser/safe_browsing/incident_reporting/module_integrity_unittest_util_win.cc
@@ -17,4 +17,6 @@ const char kTestExportName[] = "DummyExport"; +const char kTestDllMainExportName[] = "DllMain"; + } // namespace safe_browsing
diff --git a/chrome/browser/safe_browsing/incident_reporting/module_integrity_unittest_util_win.h b/chrome/browser/safe_browsing/incident_reporting/module_integrity_unittest_util_win.h index 262e015..d05183d 100644 --- a/chrome/browser/safe_browsing/incident_reporting/module_integrity_unittest_util_win.h +++ b/chrome/browser/safe_browsing/incident_reporting/module_integrity_unittest_util_win.h
@@ -18,6 +18,9 @@ // A function exported by the test dlls in |kTestDllNames|. extern const char kTestExportName[]; +// The DllMain function exported by the test dlls in |kTestDllNames|. +extern const char kTestDllMainExportName[]; + } // namespace safe_browsing #endif // CHROME_BROWSER_SAFE_BROWSING_INCIDENT_REPORTING_MODULE_INTEGRITY_UNITTEST_UTIL_WIN_H_
diff --git a/chrome/browser/safe_browsing/incident_reporting/module_integrity_verifier_win.cc b/chrome/browser/safe_browsing/incident_reporting/module_integrity_verifier_win.cc index 377efbf..6ea499b2 100644 --- a/chrome/browser/safe_browsing/incident_reporting/module_integrity_verifier_win.cc +++ b/chrome/browser/safe_browsing/incident_reporting/module_integrity_verifier_win.cc
@@ -437,6 +437,10 @@ if (!mem_peimage.VerifyMagic() || !state.disk_peimage.VerifyMagic()) return result; + // Get the list of exports and sort them by address for efficient lookups. + mem_peimage.EnumExports(EnumExportsCallback, &state.exports); + std::sort(state.exports.begin(), state.exports.end()); + // Get the addresses of the code sections then calculate |code_section_delta| // and |image_base_delta|. if (!GetCodeAddrsAndSize(mem_peimage,
diff --git a/chrome/browser/safe_browsing/incident_reporting/module_integrity_verifier_win_unittest.cc b/chrome/browser/safe_browsing/incident_reporting/module_integrity_verifier_win_unittest.cc index f9c5369..5d3d84a 100644 --- a/chrome/browser/safe_browsing/incident_reporting/module_integrity_verifier_win_unittest.cc +++ b/chrome/browser/safe_browsing/incident_reporting/module_integrity_verifier_win_unittest.cc
@@ -4,6 +4,11 @@ #include "chrome/browser/safe_browsing/incident_reporting/module_integrity_verifier_win.h" +#include <algorithm> +#include <functional> +#include <map> +#include <vector> + #include "base/files/file_path.h" #include "base/files/memory_mapped_file.h" #include "base/native_library.h" @@ -15,8 +20,51 @@ namespace safe_browsing { +// A scoper that makes a modification at a given address when constructed, and +// reverts it upon destruction. +template <size_t ModificationLength> +class ScopedModuleModifier { + public: + explicit ScopedModuleModifier(uint8_t* address) : address_(address) { + uint8_t modification[ModificationLength]; + std::transform(address, address + ModificationLength, &modification[0], + std::bind2nd(std::plus<uint8_t>(), 1U)); + SIZE_T bytes_written = 0; + EXPECT_NE(0, WriteProcessMemory(GetCurrentProcess(), + address, + &modification[0], + ModificationLength, + &bytes_written)); + EXPECT_EQ(ModificationLength, bytes_written); + } + + ~ScopedModuleModifier() { + uint8_t modification[ModificationLength]; + std::transform(address_, address_ + ModificationLength, &modification[0], + std::bind2nd(std::minus<uint8_t>(), 1U)); + SIZE_T bytes_written = 0; + EXPECT_NE(0, WriteProcessMemory(GetCurrentProcess(), + address_, + &modification[0], + ModificationLength, + &bytes_written)); + EXPECT_EQ(ModificationLength, bytes_written); + } + + private: + uint8_t* address_; + + DISALLOW_COPY_AND_ASSIGN(ScopedModuleModifier); +}; + class SafeBrowsingModuleVerifierWinTest : public testing::Test { protected: + using ModuleState = ClientIncidentReport_EnvironmentData_Process_ModuleState; + + // A mapping of an export name to the sequence of modifications for it. + using ExportNameToModifications = + std::map<std::string, std::vector<const ModuleState::Modification*>>; + void SetUpTestDllAndPEImages() { LoadModule(); HMODULE mem_handle; @@ -62,23 +110,30 @@ reinterpret_cast<HMODULE>(const_cast<uint8*>(disk_dll_handle_.data())); } - // Edits the first byte of the single function exported by the test dll. - void EditExport() { + // Returns the address of the named function exported by the test dll. + uint8_t* GetAddressOfExport(const char* export_name) { HMODULE mem_handle; GetMemModuleHandle(&mem_handle); uint8_t* export_addr = - reinterpret_cast<uint8_t*>(GetProcAddress(mem_handle, kTestExportName)); - EXPECT_NE(reinterpret_cast<uint8_t*>(NULL), export_addr); + reinterpret_cast<uint8_t*>(GetProcAddress(mem_handle, export_name)); + EXPECT_NE(nullptr, export_addr); + return export_addr; + } - // Edit the first byte of the function. - uint8_t new_val = (*export_addr) + 1; - SIZE_T bytes_written = 0; - WriteProcessMemory(GetCurrentProcess(), - export_addr, - reinterpret_cast<void*>(&new_val), - 1, - &bytes_written); - EXPECT_EQ(1, bytes_written); + // Replaces the contents of |modification_map| with pointers to those in + // |state|. |state| must outlive |modification_map|. + static void BuildModificationMap( + const ModuleState& state, + ExportNameToModifications* modification_map) { + modification_map->clear(); + std::string export_name; + for (auto& modification : state.modification()) { + if (!modification.has_export_name()) + export_name.clear(); + else + export_name = modification.export_name(); + (*modification_map)[export_name].push_back(&modification); + } } base::ScopedNativeLibrary mem_dll_handle_; @@ -125,14 +180,7 @@ // Edit the first byte of the code section of the module (this may be before // the address of any export). - uint8_t new_val = (*mem_code_addr) + 1; - SIZE_T bytes_written = 0; - WriteProcessMemory(GetCurrentProcess(), - mem_code_addr, - reinterpret_cast<void*>(&new_val), - 1, - &bytes_written); - EXPECT_EQ(1, bytes_written); + ScopedModuleModifier<1> mod(mem_code_addr); // VerifyModule should detect the change. EXPECT_EQ(MODULE_STATE_MODIFIED, @@ -150,7 +198,7 @@ // Edit the exported function, VerifyModule should now return the function // name in modified_exports. - EditExport(); + ScopedModuleModifier<1> mod(GetAddressOfExport(kTestExportName)); EXPECT_EQ(MODULE_STATE_MODIFIED, VerifyModule(kTestDllNames[0], &modified_exports, &num_bytes)); ASSERT_EQ(1, modified_exports.size()); @@ -158,7 +206,7 @@ } TEST_F(SafeBrowsingModuleVerifierWinTest, NewVerifyModuleUnmodified) { - ClientIncidentReport_EnvironmentData_Process_ModuleState state; + ModuleState state; // Call VerifyModule before the module has been loaded, should fail. VerificationResult result = NewVerifyModule(kTestDllNames[0], &state); @@ -175,7 +223,7 @@ } TEST_F(SafeBrowsingModuleVerifierWinTest, NewVerifyModuleModified) { - ClientIncidentReport_EnvironmentData_Process_ModuleState state; + ModuleState state; SetUpTestDllAndPEImages(); VerificationResult result = NewVerifyModule(kTestDllNames[0], &state); @@ -191,25 +239,11 @@ &disk_code_addr, &code_size)); - uint8_t new_first_byte = (*mem_code_addr) + 1; - SIZE_T bytes_written = 0; - WriteProcessMemory(GetCurrentProcess(), - mem_code_addr, - reinterpret_cast<void*>(&new_first_byte), - 1, - &bytes_written); - EXPECT_EQ(1, bytes_written); + ScopedModuleModifier<1> mod(mem_code_addr); size_t modification_offset = code_size - 1; - uint8_t* last_byte_addr = mem_code_addr + modification_offset; - uint8_t new_last_byte = (*last_byte_addr) + 1; - bytes_written = 0; - WriteProcessMemory(GetCurrentProcess(), - last_byte_addr, - reinterpret_cast<void*>(&new_last_byte), - 1, - &bytes_written); - EXPECT_EQ(1, bytes_written); + ScopedModuleModifier<1> mod2(mem_code_addr + modification_offset); + result = NewVerifyModule(kTestDllNames[0], &state); EXPECT_EQ(MODULE_STATE_MODIFIED, result.state); @@ -220,17 +254,19 @@ disk_code_addr - reinterpret_cast<uint8_t*>(disk_peimage_ptr_->module()); EXPECT_EQ(expected_file_offset, state.modification(0).file_offset()); EXPECT_EQ(1, state.modification(0).byte_count()); - EXPECT_EQ(new_first_byte, (uint8_t)state.modification(0).modified_bytes()[0]); + EXPECT_EQ(mem_code_addr[0], + (uint8_t)state.modification(0).modified_bytes()[0]); expected_file_offset = (disk_code_addr + modification_offset) - reinterpret_cast<uint8_t*>(disk_peimage_ptr_->module()); EXPECT_EQ(expected_file_offset, state.modification(1).file_offset()); EXPECT_EQ(1, state.modification(1).byte_count()); - EXPECT_EQ(new_last_byte, (uint8_t)state.modification(1).modified_bytes()[0]); + EXPECT_EQ(mem_code_addr[modification_offset], + (uint8_t)state.modification(1).modified_bytes()[0]); } TEST_F(SafeBrowsingModuleVerifierWinTest, NewVerifyModuleLongModification) { - ClientIncidentReport_EnvironmentData_Process_ModuleState state; + ModuleState state; SetUpTestDllAndPEImages(); VerificationResult result = NewVerifyModule(kTestDllNames[0], &state); @@ -247,43 +283,32 @@ &disk_code_addr, &code_size)); - const size_t modification_size = 256; + const size_t kModificationSize = 256; // Write the modification at the end so it's not overlapping relocations - const size_t modification_offset = code_size - modification_size; - uint8_t modification[modification_size] { 0 }; - for (size_t i = 0; i < modification_size; ++i) { - modification[i] = disk_code_addr[modification_offset + i] + 1; - } - - SIZE_T bytes_written = 0; - uint8_t* write_addr = mem_code_addr + modification_offset; - WriteProcessMemory(GetCurrentProcess(), - write_addr, - reinterpret_cast<void*>(&modification), - modification_size, - &bytes_written); - EXPECT_EQ(modification_size, bytes_written); + const size_t modification_offset = code_size - kModificationSize; + ScopedModuleModifier<kModificationSize> mod( + mem_code_addr + modification_offset); result = NewVerifyModule(kTestDllNames[0], &state); EXPECT_EQ(MODULE_STATE_MODIFIED, result.state); - EXPECT_EQ(modification_size, result.num_bytes_different); + EXPECT_EQ(kModificationSize, result.num_bytes_different); EXPECT_EQ(1, state.modification_size()); - EXPECT_EQ(modification_size, state.modification(0).byte_count()); + EXPECT_EQ(kModificationSize, state.modification(0).byte_count()); size_t expected_file_offset = disk_code_addr + modification_offset - reinterpret_cast<uint8_t*>(disk_peimage_ptr_->module()); EXPECT_EQ(expected_file_offset, state.modification(0).file_offset()); - for (size_t i = 0; i < modification_size; ++i) { - EXPECT_EQ((uint8_t)state.modification(0).modified_bytes()[i], - (uint8_t)(disk_code_addr[modification_offset + i] + 1)); - } + EXPECT_EQ( + std::string(mem_code_addr + modification_offset, + mem_code_addr + modification_offset + kModificationSize), + state.modification(0).modified_bytes()); } TEST_F(SafeBrowsingModuleVerifierWinTest, NewVerifyModuleRelocOverlap) { - ClientIncidentReport_EnvironmentData_Process_ModuleState state; + ModuleState state; SetUpTestDllAndPEImages(); VerificationResult result = NewVerifyModule(kTestDllNames[0], &state); @@ -300,35 +325,85 @@ &disk_code_addr, &code_size)); - const size_t modification_size = 256; - // Write the modification at a point where there are relocations - const size_t modification_offset = 0xF50; - uint8_t modification[modification_size] { 0 }; - for (size_t i = 0; i < modification_size; ++i) { - modification[i] = disk_code_addr[modification_offset + i] + 1; - } - - SIZE_T bytes_written = 0; - uint8_t* write_addr = mem_code_addr + modification_offset; - WriteProcessMemory(GetCurrentProcess(), - write_addr, - reinterpret_cast<void*>(&modification), - modification_size, - &bytes_written); - EXPECT_EQ(modification_size, bytes_written); + // Modify the first hunk of the code, which contains many relocs. + const size_t kModificationSize = 256; + ScopedModuleModifier<kModificationSize> mod(mem_code_addr); result = NewVerifyModule(kTestDllNames[0], &state); EXPECT_EQ(MODULE_STATE_MODIFIED, result.state); - EXPECT_EQ(modification_size, result.num_bytes_different); + EXPECT_EQ(kModificationSize, result.num_bytes_different); - // There should be more than one modification because there were relocations - // in the modified range. - EXPECT_EQ((size_t)1, state.modification_size()); - - EXPECT_EQ(modification_size, state.modification(0).byte_count()); + // Modifications across the relocs should have been coalesced into one. + ASSERT_EQ(1U, state.modification_size()); + ASSERT_EQ(kModificationSize, state.modification(0).byte_count()); + ASSERT_EQ(kModificationSize, state.modification(0).modified_bytes().size()); + EXPECT_EQ(std::string(mem_code_addr, mem_code_addr + kModificationSize), + state.modification(0).modified_bytes()); } +TEST_F(SafeBrowsingModuleVerifierWinTest, NewVerifyModuleExportModified) { + ModuleState state; + + // Confirm the module is identical in memory as on disk before we begin. + SetUpTestDllAndPEImages(); + VerificationResult result = NewVerifyModule(kTestDllNames[0], &state); + ASSERT_EQ(MODULE_STATE_UNMODIFIED, result.state); + state.Clear(); + + // Edit one exported function. VerifyModule should now return the function + // name in the modification. + ScopedModuleModifier<1> mod(GetAddressOfExport(kTestExportName)); + result = NewVerifyModule(kTestDllNames[0], &state); + EXPECT_EQ(MODULE_STATE_MODIFIED, result.state); + ASSERT_EQ(1, state.modification_size()); + + // Extract the offset of this modification. + ExportNameToModifications modification_map; + BuildModificationMap(state, &modification_map); + ASSERT_EQ(1U, modification_map[kTestExportName].size()); + uint32_t export_offset = modification_map[kTestExportName][0]->file_offset(); + + // Edit another exported function. VerifyModule should now report both. + state.Clear(); + ScopedModuleModifier<1> mod2(GetAddressOfExport(kTestDllMainExportName)); + result = NewVerifyModule(kTestDllNames[0], &state); + EXPECT_EQ(MODULE_STATE_MODIFIED, result.state); + ASSERT_EQ(2, state.modification_size()); + + // The first modification should be present and unmodified. + BuildModificationMap(state, &modification_map); + ASSERT_EQ(1U, modification_map[kTestExportName].size()); + ASSERT_EQ(export_offset, modification_map[kTestExportName][0]->file_offset()); + + // The second modification should be present and different than the first. + ASSERT_EQ(1U, modification_map[kTestDllMainExportName].size()); + ASSERT_NE(export_offset, + modification_map[kTestDllMainExportName][0]->file_offset()); + + // Now make another edit at the very end of the code section. This should be + // attributed to the last export. + uint8_t* mem_code_addr = nullptr; + uint8_t* disk_code_addr = nullptr; + uint32_t code_size = 0; + ASSERT_TRUE(GetCodeAddrsAndSize(*mem_peimage_ptr_, + *disk_peimage_ptr_, + &mem_code_addr, + &disk_code_addr, + &code_size)); + ScopedModuleModifier<1> mod3(mem_code_addr + code_size - 1); + + state.Clear(); + result = NewVerifyModule(kTestDllNames[0], &state); + EXPECT_EQ(MODULE_STATE_MODIFIED, result.state); + ASSERT_EQ(3, state.modification_size()); + + // One of the two exports now has two modifications. + BuildModificationMap(state, &modification_map); + ASSERT_EQ(2U, modification_map.size()); + ASSERT_EQ(3U, (modification_map.begin()->second.size() + + (++modification_map.begin())->second.size())); +} #endif // ADDRESS_SANITIZER } // namespace safe_browsing
diff --git a/chrome/browser/safe_browsing/incident_reporting/verifier_test/verifier_test_dll.cc b/chrome/browser/safe_browsing/incident_reporting/verifier_test/verifier_test_dll.cc index d8bdc53a..b1c0ae5 100644 --- a/chrome/browser/safe_browsing/incident_reporting/verifier_test/verifier_test_dll.cc +++ b/chrome/browser/safe_browsing/incident_reporting/verifier_test/verifier_test_dll.cc
@@ -2,14 +2,28 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// Some pointless code that will become a DLL with some exports and relocs. + #include <windows.h> -extern "C" { -// Have a dummy export so that the module gets an export table entry. -void DummyExport() { -} +namespace { + +void* g_somestate = nullptr; +int g_somevalue = 0; + +} // namespace + +extern "C" +void DummyExport(int foo) { + g_somevalue = foo; } +extern "C" BOOL APIENTRY DllMain(HMODULE module, DWORD reason, LPVOID reserved) { - return true; + if (reason == DLL_PROCESS_ATTACH) + g_somestate = &DummyExport; + else if (reason == DLL_PROCESS_DETACH) + g_somestate = nullptr; + + return TRUE; }
diff --git a/chrome/browser/safe_browsing/incident_reporting/verifier_test/verifier_test_dll_1.def b/chrome/browser/safe_browsing/incident_reporting/verifier_test/verifier_test_dll_1.def index 105afdd..78a4a167 100644 --- a/chrome/browser/safe_browsing/incident_reporting/verifier_test/verifier_test_dll_1.def +++ b/chrome/browser/safe_browsing/incident_reporting/verifier_test/verifier_test_dll_1.def
@@ -5,4 +5,5 @@ LIBRARY "verifier_test_dll_1.dll" EXPORTS + DllMain DummyExport
diff --git a/chrome/browser/safe_browsing/incident_reporting/verifier_test/verifier_test_dll_2.def b/chrome/browser/safe_browsing/incident_reporting/verifier_test/verifier_test_dll_2.def index c4208f7..b06372d5 100644 --- a/chrome/browser/safe_browsing/incident_reporting/verifier_test/verifier_test_dll_2.def +++ b/chrome/browser/safe_browsing/incident_reporting/verifier_test/verifier_test_dll_2.def
@@ -5,4 +5,5 @@ LIBRARY "verifier_test_dll_2.dll" EXPORTS + DllMain DummyExport
diff --git a/chrome/browser/safe_browsing/incident_reporting/verifier_test/verifier_unittest.gyp b/chrome/browser/safe_browsing/incident_reporting/verifier_test/verifier_unittest.gyp index 55ae891..bf93f8e 100644 --- a/chrome/browser/safe_browsing/incident_reporting/verifier_test/verifier_unittest.gyp +++ b/chrome/browser/safe_browsing/incident_reporting/verifier_test/verifier_unittest.gyp
@@ -1,4 +1,10 @@ { + 'variables': { + # Unconditionally disable incremental linking for these modules so that + # their exports do not go through an ILT jmp stub. + 'incremental_chrome_dll': '0', # 0 means no + 'msvs_debug_link_incremental': '1', # 1 means /INCREMENTAL:NO + }, 'targets': [ { 'target_name': 'verifier_test_dll_1',
diff --git a/chrome/browser/search/hotword_service.cc b/chrome/browser/search/hotword_service.cc index db9d85f..9455991 100644 --- a/chrome/browser/search/hotword_service.cc +++ b/chrome/browser/search/hotword_service.cc
@@ -271,6 +271,13 @@ l10n_util::NormalizeLocale(GetCurrentLocale(profile)); base::StringToLowerASCII(&normalized_locale); + // For M43, we are limiting always-on to en_us only. + // TODO(kcarattini): Remove this once + // https://code.google.com/p/chrome-os-partner/issues/detail?id=39227 + // is fixed. + if (HotwordServiceFactory::IsAlwaysOnAvailable()) + return normalized_locale == "en_us"; + for (size_t i = 0; i < arraysize(kSupportedLocales); i++) { if (normalized_locale == kSupportedLocales[i]) return true;
diff --git a/chrome/browser/search/hotword_service_unittest.cc b/chrome/browser/search/hotword_service_unittest.cc index 7244abf..a3aef47 100644 --- a/chrome/browser/search/hotword_service_unittest.cc +++ b/chrome/browser/search/hotword_service_unittest.cc
@@ -399,8 +399,8 @@ hotword_service->SetExtensionService(service()); hotword_service->SetExtensionId(extension_id_); - // Initialize the locale to "en". - SetApplicationLocale(profile(), "en"); + // Initialize the locale to "en_us". + SetApplicationLocale(profile(), "en_us"); // The previous locale should not be set. No reason to uninstall. EXPECT_FALSE(hotword_service->MaybeReinstallHotwordExtension()); @@ -415,13 +415,15 @@ EXPECT_FALSE(hotword_service->MaybeReinstallHotwordExtension()); EXPECT_TRUE(hotword_service->IsAlwaysOnEnabled()); - // Switch the locale to a valid but different one. - SetApplicationLocale(profile(), "fr_fr"); - EXPECT_TRUE(HotwordServiceFactory::IsHotwordAllowed(profile())); + // TODO(kcarattini): Uncomment this sectione once we launch always-on + // in more languages. + // // Switch the locale to a valid but different one. + // SetApplicationLocale(profile(), "fr_fr"); + // EXPECT_TRUE(HotwordServiceFactory::IsHotwordAllowed(profile())); - // Different but valid locale so expect uninstall. - EXPECT_TRUE(hotword_service->MaybeReinstallHotwordExtension()); - EXPECT_FALSE(hotword_service->IsAlwaysOnEnabled()); + // // Different but valid locale so expect uninstall. + // EXPECT_TRUE(hotword_service->MaybeReinstallHotwordExtension()); + // EXPECT_FALSE(hotword_service->IsAlwaysOnEnabled()); // Re-enable always-on. profile()->GetPrefs()->SetBoolean(prefs::kHotwordAlwaysOnSearchEnabled, true); @@ -432,12 +434,14 @@ EXPECT_FALSE(hotword_service->MaybeReinstallHotwordExtension()); EXPECT_TRUE(hotword_service->IsAlwaysOnEnabled()); - // If the locale is set back to the last valid one, then an uninstall-install - // shouldn't be needed. - SetApplicationLocale(profile(), "fr_fr"); - EXPECT_TRUE(HotwordServiceFactory::IsHotwordAllowed(profile())); - EXPECT_FALSE(hotword_service->MaybeReinstallHotwordExtension()); - EXPECT_TRUE(hotword_service->IsAlwaysOnEnabled()); + // TODO(kcarattini): Uncomment this sectione once we launch always-on + // in more languages. + // // If the locale is set back to the last valid one, then an + // // uninstall-install shouldn't be needed. + // SetApplicationLocale(profile(), "fr_fr"); + // EXPECT_TRUE(HotwordServiceFactory::IsHotwordAllowed(profile())); + // EXPECT_FALSE(hotword_service->MaybeReinstallHotwordExtension()); + // EXPECT_TRUE(hotword_service->IsAlwaysOnEnabled()); } TEST_P(HotwordServiceTest, IsAlwaysOnEnabled) {
diff --git a/chrome/browser/search/instant_service.cc b/chrome/browser/search/instant_service.cc index bf75e9a..573c415 100644 --- a/chrome/browser/search/instant_service.cc +++ b/chrome/browser/search/instant_service.cc
@@ -6,7 +6,7 @@ #include "chrome/browser/chrome_notification_types.h" #include "chrome/browser/favicon/fallback_icon_service_factory.h" -#include "chrome/browser/favicon/favicon_service_factory.h" +#include "chrome/browser/favicon/large_icon_service_factory.h" #include "chrome/browser/history/top_sites_factory.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/search/instant_io_context.h" @@ -25,7 +25,7 @@ #include "chrome/browser/ui/webui/theme_source.h" #include "chrome/common/render_messages.h" #include "components/favicon/core/fallback_icon_service.h" -#include "components/favicon/core/favicon_service.h" +#include "components/favicon/core/large_icon_service.h" #include "components/history/core/browser/top_sites.h" #include "components/keyed_service/core/service_access_type.h" #include "components/search_engines/template_url_service.h" @@ -118,18 +118,16 @@ content::URLDataSource::Add(profile_, new ThumbnailListSource(profile_)); #endif // !defined(OS_ANDROID) - favicon::FaviconService* favicon_service = - FaviconServiceFactory::GetForProfile(profile_, - ServiceAccessType::EXPLICIT_ACCESS); favicon::FallbackIconService* fallback_icon_service = FallbackIconServiceFactory::GetForBrowserContext(profile_); - - content::URLDataSource::Add(profile_, - new LargeIconSource(favicon_service, fallback_icon_service)); + favicon::LargeIconService* large_icon_service = + LargeIconServiceFactory::GetForBrowserContext(profile_); content::URLDataSource::Add( profile_, new FallbackIconSource(fallback_icon_service)); content::URLDataSource::Add( profile_, new FaviconSource(profile_, FaviconSource::FAVICON)); + content::URLDataSource::Add( + profile_, new LargeIconSource(fallback_icon_service, large_icon_service)); content::URLDataSource::Add(profile_, new MostVisitedIframeSource()); content::URLDataSource::Add( profile_, new suggestions::SuggestionsSource(profile_));
diff --git a/chrome/browser/services/gcm/instance_id/instance_id_profile_service.cc b/chrome/browser/services/gcm/instance_id/instance_id_profile_service.cc new file mode 100644 index 0000000..e91922a --- /dev/null +++ b/chrome/browser/services/gcm/instance_id/instance_id_profile_service.cc
@@ -0,0 +1,15 @@ +// 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 "chrome/browser/services/gcm/instance_id/instance_id_profile_service.h" + +namespace instance_id { + +InstanceIDProfileService::InstanceIDProfileService(Profile* profile) { +} + +InstanceIDProfileService::~InstanceIDProfileService() { +} + +} // namespace instance_id
diff --git a/chrome/browser/services/gcm/instance_id/instance_id_profile_service.h b/chrome/browser/services/gcm/instance_id/instance_id_profile_service.h new file mode 100644 index 0000000..7930d11 --- /dev/null +++ b/chrome/browser/services/gcm/instance_id/instance_id_profile_service.h
@@ -0,0 +1,27 @@ +// 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 CHROME_BROWSER_SERVICES_GCM_INSTANCE_ID_INSTANCE_ID_PROFILE_SERVICE_H_ +#define CHROME_BROWSER_SERVICES_GCM_INSTANCE_ID_INSTANCE_ID_PROFILE_SERVICE_H_ + +#include "base/macros.h" +#include "components/keyed_service/core/keyed_service.h" + +class Profile; + +namespace instance_id { + +// Providing Instance ID support, via InstanceIDDriver, to a profile. +class InstanceIDProfileService : public KeyedService { + public: + explicit InstanceIDProfileService(Profile* profile); + ~InstanceIDProfileService() override; + + private: + DISALLOW_COPY_AND_ASSIGN(InstanceIDProfileService); +}; + +} // namespace instance_id + +#endif // CHROME_BROWSER_SERVICES_GCM_INSTANCE_ID_INSTANCE_ID_PROFILE_SERVICE_H_
diff --git a/chrome/browser/services/gcm/instance_id/instance_id_profile_service_factory.cc b/chrome/browser/services/gcm/instance_id/instance_id_profile_service_factory.cc new file mode 100644 index 0000000..6fd67fd --- /dev/null +++ b/chrome/browser/services/gcm/instance_id/instance_id_profile_service_factory.cc
@@ -0,0 +1,55 @@ +// 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 "chrome/browser/services/gcm/instance_id/instance_id_profile_service_factory.h" + +#include "base/memory/scoped_ptr.h" +#include "chrome/browser/profiles/incognito_helpers.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/services/gcm/gcm_profile_service_factory.h" +#include "chrome/browser/services/gcm/instance_id/instance_id_profile_service.h" +#include "components/keyed_service/content/browser_context_dependency_manager.h" + +namespace instance_id { + +// static +InstanceIDProfileService* InstanceIDProfileServiceFactory::GetForProfile( + content::BrowserContext* profile) { + // Instance ID is not supported in incognito mode. + if (profile->IsOffTheRecord()) + return NULL; + + return static_cast<InstanceIDProfileService*>( + GetInstance()->GetServiceForBrowserContext(profile, true)); +} + +// static +InstanceIDProfileServiceFactory* +InstanceIDProfileServiceFactory::GetInstance() { + return Singleton<InstanceIDProfileServiceFactory>::get(); +} + +InstanceIDProfileServiceFactory::InstanceIDProfileServiceFactory() + : BrowserContextKeyedServiceFactory( + "InstanceIDProfileService", + BrowserContextDependencyManager::GetInstance()) { + // GCM is needed for device ID. + DependsOn(gcm::GCMProfileServiceFactory::GetInstance()); +} + +InstanceIDProfileServiceFactory::~InstanceIDProfileServiceFactory() { +} + +KeyedService* InstanceIDProfileServiceFactory::BuildServiceInstanceFor( + content::BrowserContext* context) const { + return new InstanceIDProfileService(Profile::FromBrowserContext(context)); +} + +content::BrowserContext* +InstanceIDProfileServiceFactory::GetBrowserContextToUse( + content::BrowserContext* context) const { + return chrome::GetBrowserContextOwnInstanceInIncognito(context); +} + +} // namespace instance_id
diff --git a/chrome/browser/services/gcm/instance_id/instance_id_profile_service_factory.h b/chrome/browser/services/gcm/instance_id/instance_id_profile_service_factory.h new file mode 100644 index 0000000..688aea7 --- /dev/null +++ b/chrome/browser/services/gcm/instance_id/instance_id_profile_service_factory.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 CHROME_BROWSER_SERVICES_GCM_INSTANCE_ID_INSTANCE_ID_PROFILE_SERVICE_FACTORY_H_ +#define CHROME_BROWSER_SERVICES_GCM_INSTANCE_ID_INSTANCE_ID_PROFILE_SERVICE_FACTORY_H_ + +#include "base/memory/singleton.h" +#include "components/keyed_service/content/browser_context_keyed_service_factory.h" + +namespace instance_id { + +class InstanceIDProfileService; + +// Singleton that owns all InstanceIDProfileService and associates them with +// profiles. +class InstanceIDProfileServiceFactory : + public BrowserContextKeyedServiceFactory { + public: + static InstanceIDProfileService* GetForProfile( + content::BrowserContext* profile); + static InstanceIDProfileServiceFactory* GetInstance(); + + private: + friend struct DefaultSingletonTraits<InstanceIDProfileServiceFactory>; + + InstanceIDProfileServiceFactory(); + ~InstanceIDProfileServiceFactory() override; + + // BrowserContextKeyedServiceFactory: + KeyedService* BuildServiceInstanceFor( + content::BrowserContext* profile) const override; + content::BrowserContext* GetBrowserContextToUse( + content::BrowserContext* context) const override; + + DISALLOW_COPY_AND_ASSIGN(InstanceIDProfileServiceFactory); +}; + +} // namespace instance_id + +#endif // CHROME_BROWSER_SERVICES_GCM_INSTANCE_ID_INSTANCE_ID_PROFILE_SERVICE_FACTORY_H_
diff --git a/chrome/browser/signin/easy_unlock_service.cc b/chrome/browser/signin/easy_unlock_service.cc index f261a35..9af5d0c 100644 --- a/chrome/browser/signin/easy_unlock_service.cc +++ b/chrome/browser/signin/easy_unlock_service.cc
@@ -27,6 +27,7 @@ #include "chrome/common/extensions/extension_constants.h" #include "chrome/common/pref_names.h" #include "components/pref_registry/pref_registry_syncable.h" +#include "components/proximity_auth/ble/proximity_auth_ble_system.h" #include "components/proximity_auth/switches.h" #include "components/user_manager/user.h" #include "device/bluetooth/bluetooth_adapter.h" @@ -646,6 +647,13 @@ app_manager_->LoadApp(); NotifyUserUpdated(); + if (base::CommandLine::ForCurrentProcess()->HasSwitch( + proximity_auth::switches::kEnableBluetoothLowEnergyDiscovery) && + !proximity_auth_ble_system_) { + proximity_auth_ble_system_.reset( + new proximity_auth::ProximityAuthBleSystem()); + } + #if defined(OS_CHROMEOS) if (!power_monitor_) power_monitor_.reset(new PowerMonitor(this)); @@ -664,6 +672,7 @@ if (!bluetooth_waking_up) { app_manager_->DisableAppIfLoaded(); ResetScreenlockState(); + proximity_auth_ble_system_.reset(); #if defined(OS_CHROMEOS) power_monitor_.reset(); #endif @@ -844,8 +853,10 @@ } void EasyUnlockService::EnsureTpmKeyPresentIfNeeded() { - if (tpm_key_checked_ || GetType() != TYPE_REGULAR || GetUserEmail().empty()) + if (tpm_key_checked_ || GetType() != TYPE_REGULAR || GetUserEmail().empty() || + GetHardlockState() == EasyUnlockScreenlockStateHandler::NO_PAIRING) { return; + } #if defined(OS_CHROMEOS) // If this is called before the session is started, the chances are Chrome
diff --git a/chrome/browser/signin/easy_unlock_service.h b/chrome/browser/signin/easy_unlock_service.h index 4824a95..969297e 100644 --- a/chrome/browser/signin/easy_unlock_service.h +++ b/chrome/browser/signin/easy_unlock_service.h
@@ -35,6 +35,10 @@ class PrefRegistrySyncable; } +namespace proximity_auth { +class ProximityAuthBleSystem; +} + class EasyUnlockAppManager; class EasyUnlockServiceObserver; class Profile; @@ -329,6 +333,11 @@ scoped_ptr<BluetoothDetector> bluetooth_detector_; + // The proximity auth over Bluetooth Low Energy system. This is main entry + // point to bootstap Smart Lock to discover phones over Bluetooth Low + // Energy. + scoped_ptr<proximity_auth::ProximityAuthBleSystem> proximity_auth_ble_system_; + #if defined(OS_CHROMEOS) // Monitors suspend and wake state of ChromeOS. class PowerMonitor;
diff --git a/chrome/browser/signin/fake_profile_oauth2_token_service.cc b/chrome/browser/signin/fake_profile_oauth2_token_service.cc index 89ba6ea..6901cdf4 100644 --- a/chrome/browser/signin/fake_profile_oauth2_token_service.cc +++ b/chrome/browser/signin/fake_profile_oauth2_token_service.cc
@@ -4,8 +4,8 @@ #include "chrome/browser/signin/fake_profile_oauth2_token_service.h" +#include "base/bind.h" #include "base/message_loop/message_loop.h" -#include "components/signin/core/browser/signin_account_id_helper.h" FakeProfileOAuth2TokenService::PendingRequest::PendingRequest() { } @@ -16,11 +16,9 @@ FakeProfileOAuth2TokenService::FakeProfileOAuth2TokenService() : auto_post_fetch_response_on_message_loop_(false), weak_ptr_factory_(this) { - SigninAccountIdHelper::SetDisableForTest(true); } FakeProfileOAuth2TokenService::~FakeProfileOAuth2TokenService() { - SigninAccountIdHelper::SetDisableForTest(false); } bool FakeProfileOAuth2TokenService::RefreshTokenIsAvailable(
diff --git a/chrome/browser/speech/tts_android.cc b/chrome/browser/speech/tts_android.cc index a9e7ee8..be3592cc 100644 --- a/chrome/browser/speech/tts_android.cc +++ b/chrome/browser/speech/tts_android.cc
@@ -30,8 +30,7 @@ TtsPlatformImplAndroid::~TtsPlatformImplAndroid() { JNIEnv* env = AttachCurrentThread(); - if (java_ref_.obj()) - Java_TtsPlatformImpl_destroy(env, java_ref_.obj()); + Java_TtsPlatformImpl_destroy(env, java_ref_.obj()); } bool TtsPlatformImplAndroid::PlatformImplAvailable() { @@ -44,9 +43,6 @@ const std::string& lang, const VoiceData& voice, const UtteranceContinuousParameters& params) { - if (!java_ref_.obj()) - return false; - JNIEnv* env = AttachCurrentThread(); jboolean success = Java_TtsPlatformImpl_speak( env, java_ref_.obj(), @@ -63,9 +59,6 @@ } bool TtsPlatformImplAndroid::StopSpeaking() { - if (!java_ref_.obj()) - return false; - JNIEnv* env = AttachCurrentThread(); Java_TtsPlatformImpl_stop(env, java_ref_.obj()); utterance_id_ = 0; @@ -86,9 +79,6 @@ void TtsPlatformImplAndroid::GetVoices( std::vector<VoiceData>* out_voices) { - if (!java_ref_.obj()) - return; - JNIEnv* env = AttachCurrentThread(); if (!Java_TtsPlatformImpl_isInitialized(env, java_ref_.obj())) return;
diff --git a/chrome/browser/spellchecker/spellcheck_platform_mac.mm b/chrome/browser/spellchecker/spellcheck_platform_mac.mm index 434c66fd..805f7ec06 100644 --- a/chrome/browser/spellchecker/spellcheck_platform_mac.mm +++ b/chrome/browser/spellchecker/spellcheck_platform_mac.mm
@@ -195,14 +195,19 @@ void FillSuggestionList(const base::string16& wrong_word, std::vector<base::string16>* optional_suggestions) { - NSString* NS_wrong_word = base::SysUTF16ToNSString(wrong_word); + NSString* ns_wrong_word = base::SysUTF16ToNSString(wrong_word); // The suggested words for |wrong_word|. - NSArray* guesses = [SharedSpellChecker() guessesForWord:NS_wrong_word]; - for (int i = 0; i < static_cast<int>([guesses count]); ++i) { - if (i < chrome::spellcheck_common::kMaxSuggestions) { - optional_suggestions->push_back(base::SysNSStringToUTF16( - [guesses objectAtIndex:i])); - } + // TODO(groby): guessesForWord: has been deprecated since OSX 10.6. + // http://www.crbug.com/479014. +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + NSArray* guesses = [SharedSpellChecker() guessesForWord:ns_wrong_word]; +#pragma clang diagnostic pop + int i = 0; + for (NSString* guess in guesses) { + optional_suggestions->push_back(base::SysNSStringToUTF16(guess)); + if (++i >= chrome::spellcheck_common::kMaxSuggestions) + break; } }
diff --git a/chrome/browser/ssl/chrome_ssl_host_state_delegate.h b/chrome/browser/ssl/chrome_ssl_host_state_delegate.h index e4e6480d..b680740 100644 --- a/chrome/browser/ssl/chrome_ssl_host_state_delegate.h +++ b/chrome/browser/ssl/chrome_ssl_host_state_delegate.h
@@ -41,7 +41,7 @@ // Revokes all SSL certificate error allow exceptions made by the user for // |host| in the given Profile. - virtual void RevokeUserAllowExceptions(const std::string& host); + void RevokeUserAllowExceptions(const std::string& host) override; // RevokeUserAllowExceptionsHard is the same as RevokeUserAllowExceptions but // additionally may close idle connections in the process. This should be used @@ -53,7 +53,7 @@ // |host|. This does not mean that *all* certificate errors are allowed, just // that there exists an exception. To see if a particular certificate and // error combination exception is allowed, use QueryPolicy(). - virtual bool HasAllowException(const std::string& host) const; + bool HasAllowException(const std::string& host) const override; protected: // SetClock takes ownership of the passed in clock.
diff --git a/chrome/browser/ssl/ssl_browser_tests.cc b/chrome/browser/ssl/ssl_browser_tests.cc index e7ded660..59da844 100644 --- a/chrome/browser/ssl/ssl_browser_tests.cc +++ b/chrome/browser/ssl/ssl_browser_tests.cc
@@ -21,6 +21,7 @@ #include "chrome/browser/safe_browsing/ping_manager.h" #include "chrome/browser/safe_browsing/safe_browsing_service.h" #include "chrome/browser/safe_browsing/ui_manager.h" +#include "chrome/browser/ssl/chrome_ssl_host_state_delegate.h" #include "chrome/browser/ssl/ssl_blocking_page.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_commands.h" @@ -41,6 +42,7 @@ #include "content/public/browser/navigation_entry.h" #include "content/public/browser/notification_service.h" #include "content/public/browser/render_frame_host.h" +#include "content/public/browser/render_process_host.h" #include "content/public/browser/render_view_host.h" #include "content/public/browser/render_widget_host_view.h" #include "content/public/browser/web_contents.h" @@ -50,13 +52,19 @@ #include "content/public/test/browser_test_utils.h" #include "content/public/test/download_test_observer.h" #include "content/public/test/test_renderer_host.h" +#include "net/base/host_port_pair.h" #include "net/base/net_errors.h" #include "net/base/test_data_directory.h" #include "net/cert/cert_status_flags.h" +#include "net/cert/test_root_certs.h" #include "net/cert/x509_certificate.h" +#include "net/dns/host_resolver.h" +#include "net/dns/mock_host_resolver.h" +#include "net/http/http_transaction_factory.h" #include "net/ssl/ssl_info.h" #include "net/test/spawned_test_server/spawned_test_server.h" #include "net/url_request/url_request_context.h" +#include "net/url_request/url_request_context_getter.h" #if defined(USE_NSS_CERTS) #include "chrome/browser/net/nss_context.h" @@ -265,6 +273,33 @@ } // namespace CertificateReporting +void RootCertsChangedOnIOThread( + const scoped_refptr<net::URLRequestContextGetter> context_getter) { + net::CertDatabase::GetInstance()->NotifyObserversOfCACertChanged(NULL); + context_getter->GetURLRequestContext() + ->http_transaction_factory() + ->GetSession() + ->CloseAllConnections(); +} + +// Alerts the URLRequestContext for the given WebContents that a root +// certificate has changed state or been removed. This, in turn, clears any +// cached certificate validation in the cert verifier. This will also close all +// connections in the socket pool of |contents|, so calls to this should be made +// with care. +void RootCertsChanged(WebContents* contents) { + scoped_refptr<net::URLRequestContextGetter> url_request_context = + contents->GetBrowserContext()->GetRequestContextForRenderProcess( + contents->GetRenderProcessHost()->GetID()); + base::RunLoop run_loop; + content::BrowserThread::PostTaskAndReply( + content::BrowserThread::IO, FROM_HERE, + base::Bind(&RootCertsChangedOnIOThread, url_request_context), + run_loop.QuitClosure()); + run_loop.Run(); + base::RunLoop().RunUntilIdle(); +} + } // namespace class SSLUITest : public InProcessBrowserTest { @@ -438,11 +473,11 @@ } static bool GetPageWithUnsafeWorkerPath( - const net::SpawnedTestServer& expired_https_server, + const net::SpawnedTestServer& https_server, std::string* page_with_unsafe_worker_path) { // Get the "imported.js" URL from the expired https server and // substitute it into the unsafe_worker.js file. - GURL imported_js_url = expired_https_server.GetURL("files/ssl/imported.js"); + GURL imported_js_url = https_server.GetURL("files/ssl/imported.js"); std::vector<net::SpawnedTestServer::StringPair> replacement_text_for_unsafe_worker; replacement_text_for_unsafe_worker.push_back( @@ -2032,32 +2067,93 @@ CheckAuthenticatedState(tab, AuthState::NONE); } -IN_PROC_BROWSER_TEST_F(SSLUITest, TestUnsafeContentsInWorker) { +// This test, and the related test TestUnsafeContentsWithUserException, verify +// that if unsafe content is loaded but the host of that unsafe content has a +// user exception, the content runs and the security style remains +// authenticated. This is not necessarily the behavior that should exist, but it +// is verification that it does behave that way. See https://crbug.com/477868 +// for more inforamtion on this. +IN_PROC_BROWSER_TEST_F(SSLUITest, TestUnsafeContentsInWorkerWithUserException) { ASSERT_TRUE(https_server_.Start()); - ASSERT_TRUE(https_server_expired_.Start()); + // Note that it is necessary to user https_server_mismatched_ here over the + // other invalid cert servers. This is because the test relies on the two + // servers having different hosts since SSL exceptions are per-host, not per + // origin, and https_server_mismatched_ uses 'localhost' rather than + // '127.0.0.1'. + ASSERT_TRUE(https_server_mismatched_.Start()); // Navigate to an unsafe site. Proceed with interstitial page to indicate // the user approves the bad certificate. - ui_test_utils::NavigateToURL(browser(), - https_server_expired_.GetURL("files/ssl/blank_page.html")); + ui_test_utils::NavigateToURL( + browser(), https_server_mismatched_.GetURL("files/ssl/blank_page.html")); WebContents* tab = browser()->tab_strip_model()->GetActiveWebContents(); - CheckAuthenticationBrokenState( - tab, net::CERT_STATUS_DATE_INVALID, AuthState::SHOWING_INTERSTITIAL); + CheckAuthenticationBrokenState(tab, net::CERT_STATUS_COMMON_NAME_INVALID, + AuthState::SHOWING_INTERSTITIAL); ProceedThroughInterstitial(tab); - CheckAuthenticationBrokenState( - tab, net::CERT_STATUS_DATE_INVALID, AuthState::NONE); + CheckAuthenticationBrokenState(tab, net::CERT_STATUS_COMMON_NAME_INVALID, + AuthState::NONE); // Navigate to safe page that has Worker loading unsafe content. // Expect content to load but be marked as auth broken due to running insecure // content. std::string page_with_unsafe_worker_path; - ASSERT_TRUE(GetPageWithUnsafeWorkerPath(https_server_expired_, + ASSERT_TRUE(GetPageWithUnsafeWorkerPath(https_server_mismatched_, &page_with_unsafe_worker_path)); - ui_test_utils::NavigateToURL(browser(), https_server_.GetURL( - page_with_unsafe_worker_path)); + ui_test_utils::NavigateToURL( + browser(), https_server_.GetURL(page_with_unsafe_worker_path)); CheckWorkerLoadResult(tab, true); // Worker loads insecure content - CheckAuthenticationBrokenState( - tab, CertError::NONE, AuthState::RAN_INSECURE_CONTENT); + CheckAuthenticatedState(tab, CertError::NONE); +} + +// Visits a page with unsafe content and makes sure that if a user exception to +// the certificate error is present, the image is loaded and script executes. +// +// See the comment above SSLUITest.TestUnsafeContentsInWorkerWithUserException +// for a discussion about the desired behavior. +IN_PROC_BROWSER_TEST_F(SSLUITest, TestUnsafeContentsWithUserException) { + ASSERT_TRUE(https_server_.Start()); + // Note that it is necessary to user https_server_mismatched_ here over the + // other invalid cert servers. This is because the test relies on the two + // servers having different hosts since SSL exceptions are per-host, not per + // origin, and https_server_mismatched_ uses 'localhost' rather than + // '127.0.0.1'. + ASSERT_TRUE(https_server_mismatched_.Start()); + + // Navigate to an unsafe site. Proceed with interstitial page to indicate + // the user approves the bad certificate. + ui_test_utils::NavigateToURL( + browser(), https_server_mismatched_.GetURL("files/ssl/blank_page.html")); + WebContents* tab = browser()->tab_strip_model()->GetActiveWebContents(); + CheckAuthenticationBrokenState(tab, net::CERT_STATUS_COMMON_NAME_INVALID, + AuthState::SHOWING_INTERSTITIAL); + ProceedThroughInterstitial(tab); + CheckAuthenticationBrokenState(tab, net::CERT_STATUS_COMMON_NAME_INVALID, + AuthState::NONE); + + std::string replacement_path; + ASSERT_TRUE(GetFilePathWithHostAndPortReplacement( + "files/ssl/page_with_unsafe_contents.html", + https_server_mismatched_.host_port_pair(), &replacement_path)); + ui_test_utils::NavigateToURL(browser(), + https_server_.GetURL(replacement_path)); + + // When the bad content is filtered, the state is expected to be + // authenticated. + CheckAuthenticatedState(tab, AuthState::NONE); + + int img_width; + EXPECT_TRUE(content::ExecuteScriptAndExtractInt( + tab, "window.domAutomationController.send(ImageWidth());", &img_width)); + // In order to check that the image was loaded, we check its width. + // The actual image (Google logo) is 114 pixels wide, so we assume a good + // image is greater than 100. + EXPECT_GT(img_width, 100); + + bool js_result = false; + EXPECT_TRUE(content::ExecuteScriptAndExtractBool( + tab, "window.domAutomationController.send(IsFooSet());", &js_result)); + EXPECT_TRUE(js_result); + CheckAuthenticatedState(tab, CertError::NONE); } // Test that when the browser blocks displaying insecure content (images), the @@ -2248,6 +2344,40 @@ EXPECT_TRUE(tab->GetRenderWidgetHostView()->IsShowing()); } +// Verifies that if a bad certificate is seen for a host and the user proceeds +// through the interstitial, the decision to proceed is initially remembered. +// However, if this is followed by another visit, and a good certificate +// is seen for the same host, the original exception is forgotten. +IN_PROC_BROWSER_TEST_F(SSLUITest, BadCertFollowedByGoodCert) { + ASSERT_TRUE(https_server_.Start()); + std::string https_server_host = + https_server_.GetURL("files/ssl/google.html").host(); + + WebContents* tab = browser()->tab_strip_model()->GetActiveWebContents(); + net::TestRootCerts* root_certs = net::TestRootCerts::GetInstance(); + + ASSERT_TRUE(root_certs); + root_certs->Clear(); + + Profile* profile = Profile::FromBrowserContext(tab->GetBrowserContext()); + ChromeSSLHostStateDelegate* state = + reinterpret_cast<ChromeSSLHostStateDelegate*>( + profile->GetSSLHostStateDelegate()); + + ui_test_utils::NavigateToURL(browser(), + https_server_.GetURL("files/ssl/google.html")); + + ProceedThroughInterstitial(tab); + EXPECT_TRUE(state->HasAllowException(https_server_host)); + + ASSERT_TRUE(https_server_.LoadTestRootCert()); + RootCertsChanged(tab); + ui_test_utils::NavigateToURL(browser(), + https_server_.GetURL("files/ssl/google.html")); + ASSERT_FALSE(tab->GetInterstitialPage()); + EXPECT_FALSE(state->HasAllowException(https_server_host)); +} + class SSLBlockingPageIDNTest : public SecurityInterstitialIDNTest { protected: // SecurityInterstitialIDNTest implementation
diff --git a/chrome/browser/supervised_user/child_accounts/child_account_service.cc b/chrome/browser/supervised_user/child_accounts/child_account_service.cc index cbc080f..ab5868f 100644 --- a/chrome/browser/supervised_user/child_accounts/child_account_service.cc +++ b/chrome/browser/supervised_user/child_accounts/child_account_service.cc
@@ -103,15 +103,19 @@ void ChildAccountService::SetIsChildAccount(bool is_child_account) { PropagateChildStatusToUser(is_child_account); - if (profile_->IsChild() == is_child_account) - return; - - if (is_child_account) { - profile_->GetPrefs()->SetString(prefs::kSupervisedUserId, - supervised_users::kChildAccountSUID); - } else { - profile_->GetPrefs()->ClearPref(prefs::kSupervisedUserId); + if (profile_->IsChild() != is_child_account) { + if (is_child_account) { + profile_->GetPrefs()->SetString(prefs::kSupervisedUserId, + supervised_users::kChildAccountSUID); + } else { + profile_->GetPrefs()->ClearPref(prefs::kSupervisedUserId); + } } + profile_->GetPrefs()->SetBoolean(prefs::kChildAccountStatusKnown, true); + + for (const auto& callback : status_received_callback_list_) + callback.Run(); + status_received_callback_list_.clear(); } void ChildAccountService::Init() { @@ -321,16 +325,6 @@ std::find(flags.begin(), flags.end(), kIsChildAccountServiceFlagName) != flags.end(); - bool status_was_known = profile_->GetPrefs()->GetBoolean( - prefs::kChildAccountStatusKnown); - profile_->GetPrefs()->SetBoolean(prefs::kChildAccountStatusKnown, true); - - if (!status_was_known) { - for (auto& callback : status_received_callback_list_) - callback.Run(); - status_received_callback_list_.clear(); - } - SetIsChildAccount(is_child_account); ScheduleNextStatusFlagUpdate(
diff --git a/chrome/browser/sync/glue/autofill_data_type_controller.cc b/chrome/browser/sync/glue/autofill_data_type_controller.cc index 87d7132..83729c58 100644 --- a/chrome/browser/sync/glue/autofill_data_type_controller.cc +++ b/chrome/browser/sync/glue/autofill_data_type_controller.cc
@@ -40,25 +40,25 @@ } void AutofillDataTypeController::WebDatabaseLoaded() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); DCHECK_EQ(MODEL_STARTING, state()); OnModelLoaded(); } AutofillDataTypeController::~AutofillDataTypeController() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); } bool AutofillDataTypeController::PostTaskOnBackendThread( const tracked_objects::Location& from_here, const base::Closure& task) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); return BrowserThread::PostTask(BrowserThread::DB, from_here, task); } bool AutofillDataTypeController::StartModels() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); DCHECK_EQ(MODEL_STARTING, state()); autofill::AutofillWebDataService* web_data_service = @@ -79,7 +79,7 @@ void AutofillDataTypeController::StartAssociating( const StartCallback& start_callback) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); DCHECK_EQ(state(), MODEL_LOADED); ProfileSyncService* sync = ProfileSyncServiceFactory::GetForProfile( profile_);
diff --git a/chrome/browser/sync/glue/autofill_profile_data_type_controller.cc b/chrome/browser/sync/glue/autofill_profile_data_type_controller.cc index ad0ec69..c1dc733b 100644 --- a/chrome/browser/sync/glue/autofill_profile_data_type_controller.cc +++ b/chrome/browser/sync/glue/autofill_profile_data_type_controller.cc
@@ -43,12 +43,12 @@ } void AutofillProfileDataTypeController::WebDatabaseLoaded() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); OnModelLoaded(); } void AutofillProfileDataTypeController::OnPersonalDataChanged() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); DCHECK_EQ(state(), MODEL_STARTING); personal_data_->RemoveObserver(this); @@ -73,12 +73,12 @@ bool AutofillProfileDataTypeController::PostTaskOnBackendThread( const tracked_objects::Location& from_here, const base::Closure& task) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); return BrowserThread::PostTask(BrowserThread::DB, from_here, task); } bool AutofillProfileDataTypeController::StartModels() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); DCHECK_EQ(state(), MODEL_STARTING); // Waiting for the personal data is subtle: we do this as the PDM resets // its cache of unique IDs once it gets loaded. If we were to proceed with @@ -110,7 +110,7 @@ } void AutofillProfileDataTypeController::StopModels() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); personal_data_->RemoveObserver(this); }
diff --git a/chrome/browser/sync/glue/autofill_wallet_data_type_controller.cc b/chrome/browser/sync/glue/autofill_wallet_data_type_controller.cc index 2f644ba..7975b26 100644 --- a/chrome/browser/sync/glue/autofill_wallet_data_type_controller.cc +++ b/chrome/browser/sync/glue/autofill_wallet_data_type_controller.cc
@@ -34,7 +34,7 @@ profile_(profile), callback_registered_(false), currently_enabled_(IsEnabled()) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); pref_registrar_.Init(profile->GetPrefs()); pref_registrar_.Add( autofill::prefs::kAutofillWalletSyncExperimentEnabled, @@ -61,12 +61,12 @@ bool AutofillWalletDataTypeController::PostTaskOnBackendThread( const tracked_objects::Location& from_here, const base::Closure& task) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); return BrowserThread::PostTask(BrowserThread::DB, from_here, task); } bool AutofillWalletDataTypeController::StartModels() { - DCHECK(content::BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); DCHECK_EQ(state(), MODEL_STARTING); autofill::AutofillWebDataService* web_data_service = @@ -89,7 +89,7 @@ } void AutofillWalletDataTypeController::StopModels() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); // This function is called when shutting down (nothing is changing), when // sync is disabled completely, or when wallet sync is disabled. In the @@ -115,7 +115,7 @@ } bool AutofillWalletDataTypeController::ReadyForStart() const { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); return currently_enabled_; } @@ -124,7 +124,7 @@ } void AutofillWalletDataTypeController::OnSyncPrefChanged() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); bool new_enabled = IsEnabled(); if (currently_enabled_ == new_enabled) @@ -154,7 +154,7 @@ } bool AutofillWalletDataTypeController::IsEnabled() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); // Require both the sync experiment and the user-visible pref to be // enabled to sync Wallet data.
diff --git a/chrome/browser/sync/glue/bookmark_change_processor.cc b/chrome/browser/sync/glue/bookmark_change_processor.cc index ad7ab852..0336a78b 100644 --- a/chrome/browser/sync/glue/bookmark_change_processor.cc +++ b/chrome/browser/sync/glue/bookmark_change_processor.cc
@@ -54,7 +54,7 @@ bookmark_model_(NULL), profile_(profile), model_associator_(model_associator) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); DCHECK(model_associator); DCHECK(profile); DCHECK(error_handler); @@ -66,7 +66,7 @@ } void BookmarkChangeProcessor::StartImpl() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); DCHECK(!bookmark_model_); bookmark_model_ = BookmarkModelFactory::GetForProfile(profile_); DCHECK(bookmark_model_->loaded()); @@ -524,7 +524,7 @@ const syncer::BaseTransaction* trans, int64 model_version, const syncer::ImmutableChangeRecordList& changes) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); // A note about ordering. Sync backend is responsible for ordering the change // records in the following order: //
diff --git a/chrome/browser/sync/glue/bookmark_data_type_controller.cc b/chrome/browser/sync/glue/bookmark_data_type_controller.cc index abed22c..45007a4 100644 --- a/chrome/browser/sync/glue/bookmark_data_type_controller.cc +++ b/chrome/browser/sync/glue/bookmark_data_type_controller.cc
@@ -107,7 +107,7 @@ void BookmarkDataTypeController::OnHistoryServiceLoaded( history::HistoryService* service) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); DCHECK_EQ(state_, MODEL_STARTING); history_service_observer_.RemoveAll();
diff --git a/chrome/browser/sync/glue/bookmark_model_associator.cc b/chrome/browser/sync/glue/bookmark_model_associator.cc index 08008ba..d24a7e7 100644 --- a/chrome/browser/sync/glue/bookmark_model_associator.cc +++ b/chrome/browser/sync/glue/bookmark_model_associator.cc
@@ -260,18 +260,18 @@ unrecoverable_error_handler_(unrecoverable_error_handler), expect_mobile_bookmarks_folder_(expect_mobile_bookmarks_folder), weak_factory_(this) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); DCHECK(bookmark_model_); DCHECK(user_share_); DCHECK(unrecoverable_error_handler_); } BookmarkModelAssociator::~BookmarkModelAssociator() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); } void BookmarkModelAssociator::UpdatePermanentNodeVisibility() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); DCHECK(bookmark_model_->loaded()); BookmarkNode::Type bookmark_node_types[] = { @@ -323,7 +323,7 @@ void BookmarkModelAssociator::Associate(const BookmarkNode* node, int64 sync_id) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); int64 node_id = node->id(); DCHECK_NE(sync_id, syncer::kInvalidId); DCHECK(id_map_.find(node_id) == id_map_.end()); @@ -336,7 +336,7 @@ } void BookmarkModelAssociator::Disassociate(int64 sync_id) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); SyncIdToBookmarkNodeMap::iterator iter = id_map_inverse_.find(sync_id); if (iter == id_map_inverse_.end()) return; @@ -582,7 +582,10 @@ &sync_child_node, parent_node, bookmark_model_, profile_, index); if (!child_node) { return unrecoverable_error_handler_->CreateAndUploadError( - FROM_HERE, "Failed to create bookmark node.", model_type()); + FROM_HERE, "Failed to create bookmark node with title " + + sync_child_node.GetTitle() + " and url " + + sync_child_node.GetBookmarkSpecifics().url(), + model_type()); } Associate(child_node, sync_child_id); local_merge_result->set_num_items_added(
diff --git a/chrome/browser/sync/glue/extension_setting_data_type_controller.cc b/chrome/browser/sync/glue/extension_setting_data_type_controller.cc index 9e015d9..8d269e5 100644 --- a/chrome/browser/sync/glue/extension_setting_data_type_controller.cc +++ b/chrome/browser/sync/glue/extension_setting_data_type_controller.cc
@@ -29,7 +29,7 @@ profile_sync_factory), type_(type), profile_(profile) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); DCHECK(type == syncer::EXTENSION_SETTINGS || type == syncer::APP_SETTINGS); } @@ -47,12 +47,12 @@ bool ExtensionSettingDataTypeController::PostTaskOnBackendThread( const tracked_objects::Location& from_here, const base::Closure& task) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); return BrowserThread::PostTask(BrowserThread::FILE, from_here, task); } bool ExtensionSettingDataTypeController::StartModels() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); extensions::ExtensionSystem::Get(profile_)->InitForRegularProfile(true); return true; }
diff --git a/chrome/browser/sync/glue/frontend_data_type_controller.cc b/chrome/browser/sync/glue/frontend_data_type_controller.cc index aee9f84..d987f742 100644 --- a/chrome/browser/sync/glue/frontend_data_type_controller.cc +++ b/chrome/browser/sync/glue/frontend_data_type_controller.cc
@@ -35,7 +35,7 @@ profile_(profile), sync_service_(sync_service), state_(NOT_RUNNING) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); DCHECK(profile_sync_factory); DCHECK(profile); DCHECK(sync_service); @@ -43,7 +43,7 @@ void FrontendDataTypeController::LoadModels( const ModelLoadCallback& model_load_callback) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); model_load_callback_ = model_load_callback; if (state_ != NOT_RUNNING) { @@ -68,7 +68,7 @@ } void FrontendDataTypeController::OnModelLoaded() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); DCHECK_EQ(state_, MODEL_STARTING); state_ = MODEL_LOADED; @@ -77,7 +77,7 @@ void FrontendDataTypeController::StartAssociating( const StartCallback& start_callback) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); DCHECK(!start_callback.is_null()); DCHECK_EQ(state_, MODEL_LOADED); @@ -89,7 +89,7 @@ } void FrontendDataTypeController::Stop() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); if (state_ == NOT_RUNNING) return; @@ -158,7 +158,7 @@ } FrontendDataTypeController::~FrontendDataTypeController() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); } bool FrontendDataTypeController::StartModels() { @@ -243,7 +243,7 @@ } void FrontendDataTypeController::AbortModelLoad() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); CleanUp(); state_ = NOT_RUNNING; } @@ -252,7 +252,7 @@ ConfigureResult start_result, const syncer::SyncMergeResult& local_merge_result, const syncer::SyncMergeResult& syncer_merge_result) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); if (!IsSuccessfulResult(start_result)) { if (IsUnrecoverableResult(start_result)) RecordUnrecoverableError(FROM_HERE, "StartFailed"); @@ -270,7 +270,7 @@ } void FrontendDataTypeController::RecordAssociationTime(base::TimeDelta time) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); #define PER_DATA_TYPE_MACRO(type_str) \ UMA_HISTOGRAM_TIMES("Sync." type_str "AssociationTime", time); SYNC_DATA_TYPE_HISTOGRAM(type()); @@ -278,7 +278,7 @@ } void FrontendDataTypeController::RecordStartFailure(ConfigureResult result) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); UMA_HISTOGRAM_ENUMERATION("Sync.DataTypeStartFailures", ModelTypeToHistogramInt(type()), syncer::MODEL_TYPE_COUNT);
diff --git a/chrome/browser/sync/glue/history_model_worker.cc b/chrome/browser/sync/glue/history_model_worker.cc index 6cda56c7..33a7334f 100644 --- a/chrome/browser/sync/glue/history_model_worker.cc +++ b/chrome/browser/sync/glue/history_model_worker.cc
@@ -70,7 +70,7 @@ base::CancelableTaskTracker* cancelable_tracker, WaitableEvent* done, syncer::SyncerError* error) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); if (history_service.get()) { scoped_ptr<history::HistoryDBTask> task(new WorkerTask(work, done, error)); history_service->ScheduleDBTask(task.Pass(), cancelable_tracker);
diff --git a/chrome/browser/sync/glue/non_frontend_data_type_controller.cc b/chrome/browser/sync/glue/non_frontend_data_type_controller.cc index b37e96a..cab1037a 100644 --- a/chrome/browser/sync/glue/non_frontend_data_type_controller.cc +++ b/chrome/browser/sync/glue/non_frontend_data_type_controller.cc
@@ -53,7 +53,7 @@ NonFrontendDataTypeController* controller) : controller_(controller), type_(controller->type()) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); controller_handle_ = syncer::MakeWeakHandle(controller_->weak_ptr_factory_.GetWeakPtr()); } @@ -173,7 +173,7 @@ model_associator_(NULL), change_processor_(NULL), weak_ptr_factory_(this) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); DCHECK(profile_sync_factory_); DCHECK(profile_); DCHECK(profile_sync_service_); @@ -181,7 +181,7 @@ void NonFrontendDataTypeController::LoadModels( const ModelLoadCallback& model_load_callback) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); model_load_callback_ = model_load_callback; if (state_ != NOT_RUNNING) { model_load_callback.Run(type(), @@ -218,7 +218,7 @@ void NonFrontendDataTypeController::StartAssociating( const StartCallback& start_callback) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); DCHECK(!start_callback.is_null()); DCHECK(!components_container_); DCHECK_EQ(state_, MODEL_LOADED); @@ -251,7 +251,7 @@ } void NonFrontendDataTypeController::Stop() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); if (state_ == NOT_RUNNING) return; @@ -313,13 +313,13 @@ } NonFrontendDataTypeController::~NonFrontendDataTypeController() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); DCHECK(!change_processor_); DCHECK(!model_associator_); } bool NonFrontendDataTypeController::StartModels() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); DCHECK_EQ(state_, MODEL_STARTING); // By default, no additional services need to be started before we can proceed // with model association, so do nothing. @@ -334,7 +334,7 @@ DataTypeController::ConfigureResult start_result, const syncer::SyncMergeResult& local_merge_result, const syncer::SyncMergeResult& syncer_merge_result) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); DataTypeController::State new_state; if (IsSuccessfulResult(start_result)) { @@ -354,7 +354,7 @@ DataTypeController::State new_state, const syncer::SyncMergeResult& local_merge_result, const syncer::SyncMergeResult& syncer_merge_result) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); state_ = new_state; if (state_ != RUNNING) { @@ -367,7 +367,7 @@ void NonFrontendDataTypeController::DisableImpl( const syncer::SyncError& error) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); if (!model_load_callback_.is_null()) { model_load_callback_.Run(type(), error); } @@ -375,7 +375,7 @@ void NonFrontendDataTypeController::RecordAssociationTime( base::TimeDelta time) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); #define PER_DATA_TYPE_MACRO(type_str) \ UMA_HISTOGRAM_TIMES("Sync." type_str "AssociationTime", time); SYNC_DATA_TYPE_HISTOGRAM(type()); @@ -383,7 +383,7 @@ } void NonFrontendDataTypeController::RecordStartFailure(ConfigureResult result) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); UMA_HISTOGRAM_ENUMERATION("Sync.DataTypeStartFailures", ModelTypeToHistogramInt(type()), syncer::MODEL_TYPE_COUNT); @@ -445,7 +445,7 @@ void NonFrontendDataTypeController::AssociationCallback( AssociationResult result) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); if (result.needs_crypto) { StartDone(NEEDS_CRYPTO,
diff --git a/chrome/browser/sync/glue/password_data_type_controller.cc b/chrome/browser/sync/glue/password_data_type_controller.cc index 71b589a..44464f0 100644 --- a/chrome/browser/sync/glue/password_data_type_controller.cc +++ b/chrome/browser/sync/glue/password_data_type_controller.cc
@@ -43,14 +43,14 @@ bool PasswordDataTypeController::PostTaskOnBackendThread( const tracked_objects::Location& from_here, const base::Closure& task) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); if (!password_store_.get()) return false; return password_store_->ScheduleTask(task); } bool PasswordDataTypeController::StartModels() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); DCHECK_EQ(MODEL_STARTING, state()); ProfileSyncService* profile_sync_service = @@ -66,7 +66,7 @@ } void PasswordDataTypeController::StopModels() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); ProfileSyncService* profile_sync_service = ProfileSyncServiceFactory::GetInstance()->GetForProfile(profile_); DCHECK(profile_sync_service);
diff --git a/chrome/browser/sync/glue/search_engine_data_type_controller.cc b/chrome/browser/sync/glue/search_engine_data_type_controller.cc index 5ac663a..161909d 100644 --- a/chrome/browser/sync/glue/search_engine_data_type_controller.cc +++ b/chrome/browser/sync/glue/search_engine_data_type_controller.cc
@@ -60,7 +60,7 @@ } void SearchEngineDataTypeController::OnTemplateURLServiceLoaded() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); DCHECK_EQ(state_, MODEL_STARTING); template_url_subscription_.reset(); OnModelLoaded();
diff --git a/chrome/browser/sync/glue/sync_backend_host_impl.cc b/chrome/browser/sync/glue/sync_backend_host_impl.cc index 26538eb1..fa0ba02 100644 --- a/chrome/browser/sync/glue/sync_backend_host_impl.cc +++ b/chrome/browser/sync/glue/sync_backend_host_impl.cc
@@ -642,7 +642,7 @@ int type, const content::NotificationSource& source, const content::NotificationDetails& details) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); DCHECK_EQ(type, chrome::NOTIFICATION_SYNC_REFRESH_LOCAL); content::Details<const syncer::ModelTypeSet> state_details(details);
diff --git a/chrome/browser/sync/glue/sync_backend_host_impl_unittest.cc b/chrome/browser/sync/glue/sync_backend_host_impl_unittest.cc index 00216ff..bbc5a4d 100644 --- a/chrome/browser/sync/glue/sync_backend_host_impl_unittest.cc +++ b/chrome/browser/sync/glue/sync_backend_host_impl_unittest.cc
@@ -260,7 +260,7 @@ } void IssueRefreshRequest(syncer::ModelTypeSet types) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); content::NotificationService::current()->Notify( chrome::NOTIFICATION_SYNC_REFRESH_LOCAL,
diff --git a/chrome/browser/sync/glue/sync_backend_registrar.cc b/chrome/browser/sync/glue/sync_backend_registrar.cc index bcddbb2..d7b22c7c 100644 --- a/chrome/browser/sync/glue/sync_backend_registrar.cc +++ b/chrome/browser/sync/glue/sync_backend_registrar.cc
@@ -61,7 +61,7 @@ scoped_ptr<base::Thread> sync_thread) : name_(name), profile_(profile) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); CHECK(profile_); // TODO(pavely): Remove ScopedTracker below once crbug.com/426272 is fixed. @@ -148,7 +148,7 @@ } bool SyncBackendRegistrar::IsNigoriEnabled() const { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); base::AutoLock lock(lock_); return routing_info_.find(syncer::NIGORI) != routing_info_.end(); } @@ -203,7 +203,7 @@ } void SyncBackendRegistrar::RequestWorkerStopOnUIThread() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); base::AutoLock lock(lock_); for (WorkerMap::const_iterator it = workers_.begin(); it != workers_.end(); ++it) {
diff --git a/chrome/browser/sync/glue/typed_url_change_processor.cc b/chrome/browser/sync/glue/typed_url_change_processor.cc index f573315..49f07fe 100644 --- a/chrome/browser/sync/glue/typed_url_change_processor.cc +++ b/chrome/browser/sync/glue/typed_url_change_processor.cc
@@ -323,7 +323,7 @@ } void TypedUrlChangeProcessor::StartImpl() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); DCHECK(history_backend_); DCHECK(backend_loop_); backend_loop_->PostTask(FROM_HERE,
diff --git a/chrome/browser/sync/glue/typed_url_data_type_controller.cc b/chrome/browser/sync/glue/typed_url_data_type_controller.cc index ab2b1a5..cef7363d 100644 --- a/chrome/browser/sync/glue/typed_url_data_type_controller.cc +++ b/chrome/browser/sync/glue/typed_url_data_type_controller.cc
@@ -93,7 +93,7 @@ } bool TypedUrlDataTypeController::ReadyForStart() const { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); return !profile()->GetPrefs()->GetBoolean( prefs::kSavingBrowserHistoryDisabled); } @@ -104,7 +104,7 @@ } void TypedUrlDataTypeController::OnSavingBrowserHistoryDisabledChanged() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); if (profile()->GetPrefs()->GetBoolean( prefs::kSavingBrowserHistoryDisabled)) { // We've turned off history persistence, so if we are running, @@ -124,7 +124,7 @@ bool TypedUrlDataTypeController::PostTaskOnBackendThread( const tracked_objects::Location& from_here, const base::Closure& task) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); history::HistoryService* history = HistoryServiceFactory::GetForProfile( profile(), ServiceAccessType::IMPLICIT_ACCESS); if (history) {
diff --git a/chrome/browser/sync/profile_sync_service_android.cc b/chrome/browser/sync/profile_sync_service_android.cc index c7350a8c..2ea5d3e 100644 --- a/chrome/browser/sync/profile_sync_service_android.cc +++ b/chrome/browser/sync/profile_sync_service_android.cc
@@ -114,7 +114,7 @@ } void ProfileSyncServiceAndroid::EnableSync(JNIEnv* env, jobject) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); // Don't need to do anything if we're already enabled. if (sync_prefs_->IsStartSuppressed()) sync_service_->UnsuppressAndStart(); @@ -123,7 +123,7 @@ } void ProfileSyncServiceAndroid::DisableSync(JNIEnv* env, jobject) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); // Don't need to do anything if we're already disabled. if (!sync_prefs_->IsStartSuppressed()) { sync_service_->StopAndSuppress(); @@ -134,7 +134,7 @@ } void ProfileSyncServiceAndroid::SignInSync(JNIEnv* env, jobject) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); // Just return if sync already has everything it needs to start up (sync // should start up automatically as long as it has credentials). This can // happen normally if (for example) the user closes and reopens the sync @@ -151,7 +151,7 @@ } void ProfileSyncServiceAndroid::SignOutSync(JNIEnv* env, jobject) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); DCHECK(profile_); sync_service_->DisableForUser(); @@ -160,13 +160,13 @@ } void ProfileSyncServiceAndroid::FlushDirectory(JNIEnv* env, jobject) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); sync_service_->FlushDirectory(); } ScopedJavaLocalRef<jstring> ProfileSyncServiceAndroid::QuerySyncStatusSummary( JNIEnv* env, jobject) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); DCHECK(profile_); std::string status(sync_service_->QuerySyncStatusSummaryString()); return ConvertUTF8ToJavaString(env, status); @@ -174,7 +174,7 @@ jboolean ProfileSyncServiceAndroid::SetSyncSessionsId( JNIEnv* env, jobject obj, jstring tag) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); DCHECK(profile_); std::string machine_tag = ConvertJavaStringToUTF8(env, tag); sync_prefs_->SetSyncSessionsGUID(machine_tag); @@ -182,41 +182,41 @@ } jint ProfileSyncServiceAndroid::GetAuthError(JNIEnv* env, jobject) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); return sync_service_->GetAuthError().state(); } jboolean ProfileSyncServiceAndroid::IsEncryptEverythingEnabled( JNIEnv* env, jobject) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); return sync_service_->EncryptEverythingEnabled(); } jboolean ProfileSyncServiceAndroid::IsSyncInitialized(JNIEnv* env, jobject) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); return sync_service_->backend_initialized(); } jboolean ProfileSyncServiceAndroid::IsFirstSetupInProgress( JNIEnv* env, jobject) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); return sync_service_->FirstSetupInProgress(); } jboolean ProfileSyncServiceAndroid::IsEncryptEverythingAllowed( JNIEnv* env, jobject obj) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); return sync_service_->EncryptEverythingAllowed(); } jboolean ProfileSyncServiceAndroid::IsPassphraseRequired(JNIEnv* env, jobject) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); return sync_service_->IsPassphraseRequired(); } jboolean ProfileSyncServiceAndroid::IsPassphraseRequiredForDecryption( JNIEnv* env, jobject obj) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); // In case of CUSTOM_PASSPHRASE we always sync passwords. Prompt the user for // a passphrase if cryptographer has any pending keys. if (sync_service_->GetPassphraseType() == syncer::CUSTOM_PASSPHRASE) { @@ -237,27 +237,27 @@ jboolean ProfileSyncServiceAndroid::IsPassphraseRequiredForExternalType( JNIEnv* env, jobject) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); return sync_service_->passphrase_required_reason() == syncer::REASON_DECRYPTION; } jboolean ProfileSyncServiceAndroid::IsUsingSecondaryPassphrase( JNIEnv* env, jobject) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); return sync_service_->IsUsingSecondaryPassphrase(); } jboolean ProfileSyncServiceAndroid::SetDecryptionPassphrase( JNIEnv* env, jobject obj, jstring passphrase) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); std::string key = ConvertJavaStringToUTF8(env, passphrase); return sync_service_->SetDecryptionPassphrase(key); } void ProfileSyncServiceAndroid::SetEncryptionPassphrase( JNIEnv* env, jobject obj, jstring passphrase, jboolean is_gaia) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); std::string key = ConvertJavaStringToUTF8(env, passphrase); sync_service_->SetEncryptionPassphrase( key, @@ -270,20 +270,20 @@ } jint ProfileSyncServiceAndroid::GetPassphraseType(JNIEnv* env, jobject) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); return sync_service_->GetPassphraseType(); } jboolean ProfileSyncServiceAndroid::HasExplicitPassphraseTime( JNIEnv* env, jobject) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); base::Time passphrase_time = sync_service_->GetExplicitPassphraseTime(); return !passphrase_time.is_null(); } jlong ProfileSyncServiceAndroid::GetExplicitPassphraseTime( JNIEnv* env, jobject) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); base::Time passphrase_time = sync_service_->GetExplicitPassphraseTime(); return passphrase_time.ToJavaTime(); } @@ -291,7 +291,7 @@ ScopedJavaLocalRef<jstring> ProfileSyncServiceAndroid::GetSyncEnterGooglePassphraseBodyWithDateText( JNIEnv* env, jobject) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); base::Time passphrase_time = sync_service_->GetExplicitPassphraseTime(); base::string16 passphrase_time_str = base::TimeFormatShortDate(passphrase_time); @@ -304,7 +304,7 @@ ScopedJavaLocalRef<jstring> ProfileSyncServiceAndroid::GetSyncEnterCustomPassphraseBodyWithDateText( JNIEnv* env, jobject) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); base::Time passphrase_time = sync_service_->GetExplicitPassphraseTime(); base::string16 passphrase_time_str = base::TimeFormatShortDate(passphrase_time); @@ -316,7 +316,7 @@ ScopedJavaLocalRef<jstring> ProfileSyncServiceAndroid::GetCurrentSignedInAccountText( JNIEnv* env, jobject) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); const std::string& sync_username = SigninManagerFactory::GetForProfile(profile_)->GetAuthenticatedUsername(); return base::android::ConvertUTF16ToJavaString(env, @@ -328,14 +328,14 @@ ScopedJavaLocalRef<jstring> ProfileSyncServiceAndroid::GetSyncEnterCustomPassphraseBodyText( JNIEnv* env, jobject) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); return ConvertUTF8ToJavaString( env, l10n_util::GetStringUTF8(IDS_SYNC_ENTER_PASSPHRASE_BODY)); } jboolean ProfileSyncServiceAndroid::IsSyncKeystoreMigrationDone( JNIEnv* env, jobject) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); syncer::SyncStatus status; bool is_status_valid = sync_service_->QueryDetailedSyncStatus(&status); return is_status_valid && !status.keystore_migration_time.is_null(); @@ -359,7 +359,7 @@ JNIEnv* env, jobject obj, jboolean sync_everything, jlong model_type_selection) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); syncer::ModelTypeSet types; // Note: only user selectable types should be included here. if (model_type_selection & AUTOFILL) @@ -378,49 +378,49 @@ void ProfileSyncServiceAndroid::SetSetupInProgress( JNIEnv* env, jobject obj, jboolean in_progress) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); sync_service_->SetSetupInProgress(in_progress); } void ProfileSyncServiceAndroid::SetSyncSetupCompleted( JNIEnv* env, jobject obj) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); sync_service_->SetSyncSetupCompleted(); } jboolean ProfileSyncServiceAndroid::HasSyncSetupCompleted( JNIEnv* env, jobject obj) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); return sync_service_->HasSyncSetupCompleted(); } jboolean ProfileSyncServiceAndroid::IsStartSuppressed( JNIEnv* env, jobject obj) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); return sync_prefs_->IsStartSuppressed(); } void ProfileSyncServiceAndroid::EnableEncryptEverything( JNIEnv* env, jobject obj) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); sync_service_->EnableEncryptEverything(); } jboolean ProfileSyncServiceAndroid::HasKeepEverythingSynced( JNIEnv* env, jobject) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); return sync_prefs_->HasKeepEverythingSynced(); } jboolean ProfileSyncServiceAndroid::HasUnrecoverableError( JNIEnv* env, jobject) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); return sync_service_->HasUnrecoverableError(); } ScopedJavaLocalRef<jstring> ProfileSyncServiceAndroid::GetAboutInfoForTest( JNIEnv* env, jobject) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); scoped_ptr<base::DictionaryValue> about_info = sync_ui_util::ConstructAboutInformation(sync_service_);
diff --git a/chrome/browser/sync/profile_sync_service_autofill_unittest.cc b/chrome/browser/sync/profile_sync_service_autofill_unittest.cc index 25082eb..e011045 100644 --- a/chrome/browser/sync/profile_sync_service_autofill_unittest.cc +++ b/chrome/browser/sync/profile_sync_service_autofill_unittest.cc
@@ -173,7 +173,7 @@ autofill::AutofillWebDataServiceObserverOnDBThread* observer) override {} void RemoveExpiredFormElements() override {} void NotifyOfMultipleAutofillChanges() override { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); + DCHECK_CURRENTLY_ON(BrowserThread::DB); BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, on_changed_); }
diff --git a/chrome/browser/sync/profile_sync_test_util.cc b/chrome/browser/sync/profile_sync_test_util.cc index 565c5ad8..087a4d2 100644 --- a/chrome/browser/sync/profile_sync_test_util.cc +++ b/chrome/browser/sync/profile_sync_test_util.cc
@@ -27,7 +27,7 @@ void ThreadNotifier::Notify(int type, const content::NotificationSource& source, const content::NotificationDetails& details) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); notify_thread_->message_loop()->PostTask( FROM_HERE, base::Bind(&ThreadNotifier::NotifyTask, this, type, source, details));
diff --git a/chrome/browser/sync/profile_sync_test_util.h b/chrome/browser/sync/profile_sync_test_util.h index b2692df..2fb427476 100644 --- a/chrome/browser/sync/profile_sync_test_util.h +++ b/chrome/browser/sync/profile_sync_test_util.h
@@ -29,7 +29,7 @@ } ACTION(QuitUIMessageLoop) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); base::MessageLoop::current()->Quit(); }
diff --git a/chrome/browser/sync/sessions/session_data_type_controller.cc b/chrome/browser/sync/sessions/session_data_type_controller.cc index f3d43ba..9eb90ae4 100644 --- a/chrome/browser/sync/sessions/session_data_type_controller.cc +++ b/chrome/browser/sync/sessions/session_data_type_controller.cc
@@ -46,7 +46,7 @@ SessionDataTypeController::~SessionDataTypeController() {} bool SessionDataTypeController::StartModels() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); std::set<const browser_sync::SyncedWindowDelegate*> window = synced_window_getter_->GetSyncedWindowDelegates(); for (std::set<const browser_sync::SyncedWindowDelegate*>::const_iterator i = @@ -77,7 +77,7 @@ } bool SessionDataTypeController::ReadyForStart() const { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); return !profile_->GetPrefs()->GetBoolean( prefs::kSavingBrowserHistoryDisabled); } @@ -96,7 +96,7 @@ int type, const content::NotificationSource& source, const content::NotificationDetails& details) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); DCHECK_EQ(chrome::NOTIFICATION_SESSION_RESTORE_COMPLETE, type); DCHECK_EQ(profile_, content::Source<Profile>(source).ptr()); notification_registrar_.RemoveAll(); @@ -106,7 +106,7 @@ } void SessionDataTypeController::OnLocalDeviceInfoInitialized() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); subscription_.reset(); waiting_on_local_device_info_ = false; @@ -114,7 +114,7 @@ } void SessionDataTypeController::OnSavingBrowserHistoryPrefChanged() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); if (profile_->GetPrefs()->GetBoolean(prefs::kSavingBrowserHistoryDisabled)) { // If history and tabs persistence is turned off then generate an // unrecoverable error. SESSIONS won't be a registered type on the next
diff --git a/chrome/browser/sync/test/integration/autofill_helper.cc b/chrome/browser/sync/test/integration/autofill_helper.cc index b5877fc5..957de84 100644 --- a/chrome/browser/sync/test/integration/autofill_helper.cc +++ b/chrome/browser/sync/test/integration/autofill_helper.cc
@@ -101,14 +101,14 @@ void GetAllAutofillEntriesOnDBThread(AutofillWebDataService* wds, std::vector<AutofillEntry>* entries) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); + DCHECK_CURRENTLY_ON(BrowserThread::DB); AutofillTable::FromWebDatabase( wds->GetDatabase())->GetAllAutofillEntries(entries); } std::vector<AutofillEntry> GetAllAutofillEntries(AutofillWebDataService* wds) { std::vector<AutofillEntry> entries; - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); RunOnDBThreadAndBlock(Bind(&GetAllAutofillEntriesOnDBThread, Unretained(wds), &entries));
diff --git a/chrome/browser/sync_file_system/local/local_file_sync_service.cc b/chrome/browser/sync_file_system/local/local_file_sync_service.cc index 51ab55a..b9c76f74 100644 --- a/chrome/browser/sync_file_system/local/local_file_sync_service.cc +++ b/chrome/browser/sync_file_system/local/local_file_sync_service.cc
@@ -120,7 +120,7 @@ } LocalFileSyncService::~LocalFileSyncService() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); } void LocalFileSyncService::Shutdown() { @@ -350,7 +350,7 @@ BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO) .get())), local_change_processor_(nullptr) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); sync_context_->AddOriginChangeObserver(this); }
diff --git a/chrome/browser/sync_file_system/sync_file_system_service.cc b/chrome/browser/sync_file_system/sync_file_system_service.cc index 777c8c5..804eccf 100644 --- a/chrome/browser/sync_file_system/sync_file_system_service.cc +++ b/chrome/browser/sync_file_system/sync_file_system_service.cc
@@ -158,7 +158,7 @@ // LocalFileSyncService::Observer overrides. void OnLocalChangeAvailable(int64 pending_changes) override { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); OnChangesUpdated(pending_changes); @@ -208,7 +208,7 @@ // RemoteFileSyncService::Observer overrides. void OnRemoteChangeQueueUpdated(int64 pending_changes) override { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); OnChangesUpdated(pending_changes); @@ -251,7 +251,7 @@ // SyncFileSystemService void SyncFileSystemService::Shutdown() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); local_sync_runners_.clear(); remote_sync_runners_.clear(); @@ -272,7 +272,7 @@ } SyncFileSystemService::~SyncFileSystemService() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); DCHECK(!profile_); } @@ -446,7 +446,7 @@ void SyncFileSystemService::Initialize( scoped_ptr<LocalFileSyncService> local_service, scoped_ptr<RemoteFileSyncService> remote_service) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); DCHECK(local_service); DCHECK(remote_service); DCHECK(profile_); @@ -635,7 +635,7 @@ void SyncFileSystemService::OnRemoteServiceStateUpdated( RemoteServiceState state, const std::string& description) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); util::Log(logging::LOG_VERBOSE, FROM_HERE, "OnRemoteServiceStateChanged: %d %s", state, description.c_str());
diff --git a/chrome/browser/task_management/providers/child_process_task_provider.cc b/chrome/browser/task_management/providers/child_process_task_provider.cc index 59db47f34..a6f2073a 100644 --- a/chrome/browser/task_management/providers/child_process_task_provider.cc +++ b/chrome/browser/task_management/providers/child_process_task_provider.cc
@@ -24,7 +24,7 @@ // |BrowserChildProcessObserver|. scoped_ptr<std::vector<ChildProcessData>> CollectChildProcessData() { // The |BrowserChildProcessHostIterator| must only be used on the IO thread. - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + DCHECK_CURRENTLY_ON(BrowserThread::IO); scoped_ptr<std::vector<ChildProcessData>> child_processes( new std::vector<ChildProcessData>()); @@ -55,7 +55,7 @@ Task* ChildProcessTaskProvider::GetTaskOfUrlRequest(int origin_pid, int child_id, int route_id) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); auto itr = tasks_by_pid_.find(static_cast<base::ProcessId>(origin_pid)); if (itr == tasks_by_pid_.end()) return nullptr; @@ -65,7 +65,7 @@ void ChildProcessTaskProvider::BrowserChildProcessHostConnected( const content::ChildProcessData& data) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); if (data.handle == base::kNullProcessHandle) return; @@ -74,12 +74,12 @@ void ChildProcessTaskProvider::BrowserChildProcessHostDisconnected( const content::ChildProcessData& data) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); DeleteTask(data.handle); } void ChildProcessTaskProvider::StartUpdating() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); DCHECK(tasks_by_handle_.empty()); DCHECK(tasks_by_pid_.empty()); @@ -93,7 +93,7 @@ } void ChildProcessTaskProvider::StopUpdating() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); // ChildProcessDataCollected() should never be called after this, and hence // we must invalidate the weak pointers. @@ -112,7 +112,7 @@ void ChildProcessTaskProvider::ChildProcessDataCollected( scoped_ptr<const std::vector<content::ChildProcessData>> child_processes) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); for (auto& process_data : *child_processes) CreateTask(process_data);
diff --git a/chrome/browser/themes/browser_theme_pack.cc b/chrome/browser/themes/browser_theme_pack.cc index 8175584..53f5de7 100644 --- a/chrome/browser/themes/browser_theme_pack.cc +++ b/chrome/browser/themes/browser_theme_pack.cc
@@ -651,7 +651,7 @@ // static scoped_refptr<BrowserThemePack> BrowserThemePack::BuildFromExtension( const Extension* extension) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); DCHECK(extension); DCHECK(extension->is_theme()); @@ -712,7 +712,7 @@ // static scoped_refptr<BrowserThemePack> BrowserThemePack::BuildFromDataPack( const base::FilePath& path, const std::string& expected_id) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); // Allow IO on UI thread due to deep-seated theme design issues. // (see http://crbug.com/80206) base::ThreadRestrictions::ScopedAllowIO allow_io;
diff --git a/chrome/browser/thumbnails/content_based_thumbnailing_algorithm.cc b/chrome/browser/thumbnails/content_based_thumbnailing_algorithm.cc index 9d6222f..6278009 100644 --- a/chrome/browser/thumbnails/content_based_thumbnailing_algorithm.cc +++ b/chrome/browser/thumbnails/content_based_thumbnailing_algorithm.cc
@@ -60,7 +60,7 @@ scoped_refptr<ThumbnailingContext> context, const ConsumerCallback& callback, const SkBitmap& bitmap) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); DCHECK(context.get()); if (bitmap.isNull() || bitmap.empty()) return;
diff --git a/chrome/browser/thumbnails/simple_thumbnail_crop.cc b/chrome/browser/thumbnails/simple_thumbnail_crop.cc index 67774d55..102fd61 100644 --- a/chrome/browser/thumbnails/simple_thumbnail_crop.cc +++ b/chrome/browser/thumbnails/simple_thumbnail_crop.cc
@@ -41,7 +41,7 @@ scoped_refptr<ThumbnailingContext> context, const ConsumerCallback& callback, const SkBitmap& bitmap) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); if (bitmap.isNull() || bitmap.empty()) return;
diff --git a/chrome/browser/thumbnails/thumbnail_service_impl.cc b/chrome/browser/thumbnails/thumbnail_service_impl.cc index 29ff5ec..ee6cf7e 100644 --- a/chrome/browser/thumbnails/thumbnail_service_impl.cc +++ b/chrome/browser/thumbnails/thumbnail_service_impl.cc
@@ -36,7 +36,7 @@ void AddForcedURLOnUIThread(scoped_refptr<history::TopSites> top_sites, const GURL& url) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); if (top_sites) top_sites->AddForcedURL(url, base::Time::Now());
diff --git a/chrome/browser/thumbnails/thumbnail_tab_helper.cc b/chrome/browser/thumbnails/thumbnail_tab_helper.cc index 0db6199..f8322e7 100644 --- a/chrome/browser/thumbnails/thumbnail_tab_helper.cc +++ b/chrome/browser/thumbnails/thumbnail_tab_helper.cc
@@ -71,7 +71,7 @@ // On success, we must be on the UI thread (on failure because of shutdown we // are not on the UI thread). - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); algorithm->ProcessBitmap(context, base::Bind(&UpdateThumbnail), bitmap); } @@ -79,7 +79,7 @@ void AsyncProcessThumbnail(content::WebContents* web_contents, scoped_refptr<ThumbnailingContext> context, scoped_refptr<ThumbnailingAlgorithm> algorithm) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); RenderWidgetHost* render_widget_host = web_contents->GetRenderViewHost(); content::RenderWidgetHostView* view = render_widget_host->GetView(); if (!view)
diff --git a/chrome/browser/ui/app_list/search/common/url_icon_source.cc b/chrome/browser/ui/app_list/search/common/url_icon_source.cc index 3dccf53..daa4046 100644 --- a/chrome/browser/ui/app_list/search/common/url_icon_source.cc +++ b/chrome/browser/ui/app_list/search/common/url_icon_source.cc
@@ -22,9 +22,7 @@ const GURL& icon_url, int icon_size, int default_icon_resource_id) - : ImageRequest( - BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI)), - icon_loaded_callback_(icon_loaded_callback), + : icon_loaded_callback_(icon_loaded_callback), context_getter_(context_getter), icon_url_(icon_url), icon_size_(icon_size),
diff --git a/chrome/browser/ui/ash/system_tray_delegate_chromeos.cc b/chrome/browser/ui/ash/system_tray_delegate_chromeos.cc index f3ef877..c70275f 100644 --- a/chrome/browser/ui/ash/system_tray_delegate_chromeos.cc +++ b/chrome/browser/ui/ash/system_tray_delegate_chromeos.cc
@@ -58,6 +58,8 @@ #include "chrome/browser/chromeos/login/users/chrome_user_manager.h" #include "chrome/browser/chromeos/login/users/supervised_user_manager.h" #include "chrome/browser/chromeos/options/network_config_view.h" +#include "chrome/browser/chromeos/ownership/owner_settings_service_chromeos.h" +#include "chrome/browser/chromeos/ownership/owner_settings_service_chromeos_factory.h" #include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h" #include "chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos.h" #include "chrome/browser/chromeos/profiles/multiprofiles_intro_dialog.h" @@ -1018,8 +1020,17 @@ GetSystemTrayNotifier()->NotifyDateFormatChanged(); // This also works for enterprise-managed devices because they never have // local owner. - if (user_manager::UserManager::Get()->IsCurrentUserOwner()) - CrosSettings::Get()->SetBoolean(kSystemUse24HourClock, use_24_hour_clock); + if (user_manager::UserManager::Get()->IsCurrentUserOwner()) { + user_manager::User* const user = + user_manager::UserManager::Get()->GetActiveUser(); + CHECK(user); + Profile* const profile = ProfileHelper::Get()->GetProfileByUser(user); + CHECK(profile); + OwnerSettingsServiceChromeOS* const service = + OwnerSettingsServiceChromeOSFactory::GetForBrowserContext(profile); + CHECK(service); + service->SetBoolean(kSystemUse24HourClock, use_24_hour_clock); + } } void SystemTrayDelegateChromeOS::UpdateShowLogoutButtonInTray() { @@ -1120,8 +1131,15 @@ // user's profile has actually been loaded (http://crbug.com/317745). The // system tray's time format is updated at login via SetProfile(). if (user_manager::UserManager::Get()->IsCurrentUserOwner()) { - CrosSettings::Get()->SetBoolean(kSystemUse24HourClock, - ShouldUse24HourClock()); + user_manager::User* const user = + user_manager::UserManager::Get()->GetActiveUser(); + CHECK(user); + Profile* const profile = ProfileHelper::Get()->GetProfileByUser(user); + CHECK(profile); + OwnerSettingsServiceChromeOS* const service = + OwnerSettingsServiceChromeOSFactory::GetForBrowserContext(profile); + CHECK(service); + service->SetBoolean(kSystemUse24HourClock, ShouldUse24HourClock()); } }
diff --git a/chrome/browser/ui/browser_browsertest.cc b/chrome/browser/ui/browser_browsertest.cc index 56ccdce..ac03700f 100644 --- a/chrome/browser/ui/browser_browsertest.cc +++ b/chrome/browser/ui/browser_browsertest.cc
@@ -2074,9 +2074,6 @@ } #endif -// TODO(schenney) Disable on Mac to enable a Blink roll. -// Re-enable and fix after Blink rolls. -#if !defined(OS_MACOSX) IN_PROC_BROWSER_TEST_F(BrowserTest, WindowOpenClose) { base::CommandLine::ForCurrentProcess()->AppendSwitch( switches::kDisablePopupBlocking); @@ -2089,7 +2086,6 @@ ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(browser(), url, 2); EXPECT_EQ(title, title_watcher.WaitAndGetTitle()); } -#endif // TODO(linux_aura) http://crbug.com/163931 // Mac disabled: http://crbug.com/169820
diff --git a/chrome/browser/ui/browser_command_controller_browsertest.cc b/chrome/browser/ui/browser_command_controller_browsertest.cc index 8a43633..feb5af2 100644 --- a/chrome/browser/ui/browser_command_controller_browsertest.cc +++ b/chrome/browser/ui/browser_command_controller_browsertest.cc
@@ -75,15 +75,9 @@ // Note that a Browser's destructor, when the browser's profile is guest, will // create and execute a BrowsingDataRemover. -// Flakes on Linux: http://crbug.com/471953 -#if defined(OS_LINUX) -#define MAYBE_NewAvatarMenuEnabledInGuestMode \ - DISABLED_NewAvatarMenuEnabledInGuestMode -#else -#define MAYBE_NewAvatarMenuEnabledInGuestMode NewAvatarMenuEnabledInGuestMode -#endif +// Flakes http://crbug.com/471953 IN_PROC_BROWSER_TEST_F(BrowserCommandControllerBrowserTest, - MAYBE_NewAvatarMenuEnabledInGuestMode) { + DISABLED_NewAvatarMenuEnabledInGuestMode) { switches::EnableNewAvatarMenuForTesting( base::CommandLine::ForCurrentProcess());
diff --git a/chrome/browser/ui/cocoa/apps/native_app_window_cocoa.mm b/chrome/browser/ui/cocoa/apps/native_app_window_cocoa.mm index 6744a8c..84e1618 100644 --- a/chrome/browser/ui/cocoa/apps/native_app_window_cocoa.mm +++ b/chrome/browser/ui/cocoa/apps/native_app_window_cocoa.mm
@@ -855,8 +855,10 @@ } void NativeAppWindowCocoa::WindowDidEnterFullscreen() { - is_fullscreen_ = true; - app_window_->OSFullscreen(); + if (!is_fullscreen_) { + is_fullscreen_ = true; + app_window_->OSFullscreen(); + } app_window_->OnNativeWindowChanged(); }
diff --git a/chrome/browser/ui/cocoa/bookmarks/bookmark_tree_browser_cell.h b/chrome/browser/ui/cocoa/bookmarks/bookmark_tree_browser_cell.h index 25479f9..483270e 100644 --- a/chrome/browser/ui/cocoa/bookmarks/bookmark_tree_browser_cell.h +++ b/chrome/browser/ui/cocoa/bookmarks/bookmark_tree_browser_cell.h
@@ -20,13 +20,24 @@ @private const bookmarks::BookmarkNode* bookmarkNode_; // weak NSMatrix* matrix_; // weak + + // NSCell does not implement the |target| or |action| properties. Subclasses + // that need this functionality are expected to implement this functionality. id target_; // weak SEL action_; } @property(nonatomic, assign) NSMatrix* matrix; + +#if !defined(MAC_OS_X_VERSION_10_10) || \ + MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_10 +// In OSX SDK <= 10.9, there are setters and getters for target and action in +// NSCell, but no properties. In the OSX 10.10 SDK, the properties are defined +// as atomic. There is no point in redeclaring the properties if they already +// exist. @property(nonatomic, assign) id target; @property(nonatomic, assign) SEL action; +#endif // MAC_OS_X_VERSION_10_10 - (const bookmarks::BookmarkNode*)bookmarkNode; - (void)setBookmarkNode:(const bookmarks::BookmarkNode*)bookmarkNode;
diff --git a/chrome/browser/ui/cocoa/extensions/browser_actions_controller.h b/chrome/browser/ui/cocoa/extensions/browser_actions_controller.h index af71cd3..073e7dd 100644 --- a/chrome/browser/ui/cocoa/extensions/browser_actions_controller.h +++ b/chrome/browser/ui/cocoa/extensions/browser_actions_controller.h
@@ -16,6 +16,7 @@ @class BrowserActionsContainerView; @class MenuButton; class ToolbarActionsBar; +@class ToolbarActionsBarBubbleMac; class ToolbarActionsBarDelegate; namespace content { @@ -60,11 +61,15 @@ // The Browser Actions overflow menu. base::scoped_nsobject<NSMenu> overflowMenu_; + + // The bubble that is actively showing, if any. + ToolbarActionsBarBubbleMac* activeBubble_; } @property(readonly, nonatomic) BrowserActionsContainerView* containerView; @property(readonly, nonatomic) Browser* browser; @property(readonly, nonatomic) BOOL isOverflow; +@property(readonly, nonatomic) ToolbarActionsBarBubbleMac* activeBubble; // Initializes the controller given the current browser and container view that // will hold the browser action buttons. If |mainController| is nil, the created
diff --git a/chrome/browser/ui/cocoa/extensions/browser_actions_controller.mm b/chrome/browser/ui/cocoa/extensions/browser_actions_controller.mm index ff5a3b2..10ae632 100644 --- a/chrome/browser/ui/cocoa/extensions/browser_actions_controller.mm +++ b/chrome/browser/ui/cocoa/extensions/browser_actions_controller.mm
@@ -111,6 +111,10 @@ // Returns the frame that the button with the given |index| should have. - (NSRect)frameForIndex:(NSUInteger)index; +// Returns the popup point for the given |view| with |bounds|. +- (NSPoint)popupPointForView:(NSView*)view + withBounds:(NSRect)bounds; + // Moves the given button both visually and within the toolbar model to the // specified index. - (void)moveButton:(BrowserActionButton*)button @@ -149,6 +153,10 @@ - (ToolbarActionsBarBubbleMac*)createMessageBubble: (scoped_ptr<ToolbarActionsBarBubbleDelegate>)delegate; +// Called when the window for the active bubble is closing, and sets the active +// bubble to nil. +- (void)bubbleWindowClosing:(NSNotification*)notification; + @end namespace { @@ -281,6 +289,7 @@ @synthesize containerView = containerView_; @synthesize browser = browser_; @synthesize isOverflow = isOverflow_; +@synthesize activeBubble = activeBubble_; #pragma mark - #pragma mark Public Methods @@ -403,17 +412,7 @@ fromView:[button superview]]; } - // Anchor point just above the center of the bottom. - DCHECK([referenceButton isFlipped]); - NSPoint anchor = NSMakePoint(NSMidX(bounds), - NSMaxY(bounds) - kBrowserActionBubbleYOffset); - // Convert the point to the container view's frame, and adjust for animation. - NSPoint anchorInContainer = - [containerView_ convertPoint:anchor fromView:referenceButton]; - anchorInContainer.x -= NSMinX([containerView_ frame]) - - NSMinX([containerView_ animationEndFrame]); - - return [containerView_ convertPoint:anchorInContainer toView:nil]; + return [self popupPointForView:referenceButton withBounds:bounds]; } - (BOOL)chevronIsHidden { @@ -512,11 +511,18 @@ std::vector<ToolbarActionViewController*> toolbar_actions = toolbarActionsBar_->toolbar_actions(); for (NSUInteger i = 0; i < [buttons_ count]; ++i) { - if ([[buttons_ objectAtIndex:i] viewController] != toolbar_actions[i]) { + auto controller = static_cast<ToolbarActionViewController*>( + [[buttons_ objectAtIndex:i] viewController]); + if (controller != toolbar_actions[i]) { size_t j = i + 1; - while (toolbar_actions[i] != [[buttons_ objectAtIndex:j] viewController]) + while (true) { + auto other_controller = static_cast<ToolbarActionViewController*>( + [[buttons_ objectAtIndex:j] viewController]); + if (other_controller == toolbar_actions[i]) + break; ++j; - [buttons_ exchangeObjectAtIndex:i withObjectAtIndex: j]; + } + [buttons_ exchangeObjectAtIndex:i withObjectAtIndex:j]; } } @@ -691,7 +697,8 @@ } - (void)containerMouseEntered:(NSNotification*)notification { - if (ExtensionToolbarIconSurfacingBubbleDelegate::ShouldShowForProfile( + if (!activeBubble_ && // only show one bubble at a time + ExtensionToolbarIconSurfacingBubbleDelegate::ShouldShowForProfile( browser_->profile())) { ToolbarActionsBarBubbleMac* bubble = [self createMessageBubble:scoped_ptr<ToolbarActionsBarBubbleDelegate>( @@ -766,6 +773,21 @@ ToolbarActionsBar::IconHeight()); } +- (NSPoint)popupPointForView:(NSView*)view + withBounds:(NSRect)bounds { + // Anchor point just above the center of the bottom. + DCHECK([view isFlipped]); + NSPoint anchor = NSMakePoint(NSMidX(bounds), + NSMaxY(bounds) - kBrowserActionBubbleYOffset); + // Convert the point to the container view's frame, and adjust for animation. + NSPoint anchorInContainer = + [containerView_ convertPoint:anchor fromView:view]; + anchorInContainer.x -= NSMinX([containerView_ frame]) - + NSMinX([containerView_ animationEndFrame]); + + return [containerView_ convertPoint:anchorInContainer toView:nil]; +} + - (void)moveButton:(BrowserActionButton*)button toIndex:(NSUInteger)index { NSRect buttonFrame = [self frameForIndex:index]; @@ -889,15 +911,33 @@ - (ToolbarActionsBarBubbleMac*)createMessageBubble: (scoped_ptr<ToolbarActionsBarBubbleDelegate>)delegate { DCHECK_GE([buttons_ count], 0u); - NSPoint anchor = [self popupPointForId:[[buttons_ objectAtIndex:0] - viewController]->GetId()]; + NSPoint anchor; + if ([buttons_ count] > 0) { + auto controller = static_cast<ToolbarActionViewController*>( + [[buttons_ objectAtIndex:0] viewController]); + anchor = [self popupPointForId:controller->GetId()]; + } else { + NSView* wrenchButton = [[self toolbarController] wrenchButton]; + anchor = [self popupPointForView:wrenchButton + withBounds:[wrenchButton bounds]]; + } + anchor = [[containerView_ window] convertBaseToScreen:anchor]; - return [[ToolbarActionsBarBubbleMac alloc] + activeBubble_ = [[ToolbarActionsBarBubbleMac alloc] initWithParentWindow:[containerView_ window] anchorPoint:anchor delegate:delegate.Pass()]; + [[NSNotificationCenter defaultCenter] + addObserver:self + selector:@selector(bubbleWindowClosing:) + name:NSWindowWillCloseNotification + object:[activeBubble_ window]]; + return activeBubble_; } +- (void)bubbleWindowClosing:(NSNotification*)notification { + activeBubble_ = nil; +} #pragma mark - #pragma mark Testing Methods
diff --git a/chrome/browser/ui/cocoa/extensions/extension_message_bubble_browsertest_mac.mm b/chrome/browser/ui/cocoa/extensions/extension_message_bubble_browsertest_mac.mm new file mode 100644 index 0000000..2579ed5 --- /dev/null +++ b/chrome/browser/ui/cocoa/extensions/extension_message_bubble_browsertest_mac.mm
@@ -0,0 +1,131 @@ +// 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 "chrome/browser/ui/browser_window.h" +#import "chrome/browser/ui/cocoa/browser_window_controller.h" +#import "chrome/browser/ui/cocoa/extensions/browser_action_button.h" +#import "chrome/browser/ui/cocoa/extensions/browser_actions_controller.h" +#import "chrome/browser/ui/cocoa/extensions/toolbar_actions_bar_bubble_mac.h" +#import "chrome/browser/ui/cocoa/toolbar/toolbar_controller.h" +#include "chrome/browser/ui/extensions/extension_message_bubble_browsertest.h" + +namespace { + +// Returns the ToolbarController for the given browser. +ToolbarController* ToolbarControllerForBrowser(Browser* browser) { + return [[BrowserWindowController browserWindowControllerForWindow: + browser->window()->GetNativeWindow()] toolbarController]; +} + +// Checks that the |bubble| is using the |expectedReferenceView|, and is in +// approximately the correct position. +void CheckBubbleAndReferenceView(ToolbarActionsBarBubbleMac* bubble, + NSView* expectedReferenceView) { + ASSERT_TRUE(bubble); + ASSERT_TRUE(expectedReferenceView); + + // Do a rough check that the bubble is in the right place. + // A window's frame (like the bubble's) is already in screen coordinates. + NSRect bubbleFrame = [[bubble window] frame]; + + // Unfortunately, it's more tedious to get the reference view's screen + // coordinates (since -[NSWindow convertRectToScreen is in OSX 10.7). + NSRect referenceFrame = [expectedReferenceView bounds]; + referenceFrame = + [expectedReferenceView convertRect:referenceFrame toView:nil]; + NSWindow* window = [expectedReferenceView window]; + CGFloat refLowY = [expectedReferenceView isFlipped] ? + NSMaxY(referenceFrame) : NSMinY(referenceFrame); + NSPoint refLowerLeft = NSMakePoint(NSMinX(referenceFrame), refLowY); + NSPoint refLowerLeftInScreen = [window convertBaseToScreen:refLowerLeft]; + NSPoint refLowerRight = NSMakePoint(NSMaxX(referenceFrame), refLowY); + NSPoint refLowerRightInScreen = [window convertBaseToScreen:refLowerRight]; + + // The bubble should be below the reference view, but not too far below. + EXPECT_LE(NSMaxY(bubbleFrame), refLowerLeftInScreen.y); + // "Too far below" is kind of ambiguous. The exact logic of where a bubble + // is positioned with respect to its anchor view should be tested as part of + // the bubble logic, but we still want to make sure we didn't accidentally + // place it somewhere crazy (which can happen if we draw it, and then + // animate or reposition the reference view). + const int kFudgeFactor = 50; + EXPECT_LE(NSMaxY(bubbleFrame), refLowerLeftInScreen.y + kFudgeFactor); + + // The bubble should intersect the reference view somewhere along the x-axis. + EXPECT_LE(refLowerLeftInScreen.x, NSMaxX(bubbleFrame)); + EXPECT_LE(NSMinX(bubbleFrame), refLowerRightInScreen.x); + + // And, of course, the bubble should be visible. + EXPECT_TRUE([[bubble window] isVisible]); +} + +} // namespace + +class ExtensionMessageBubbleBrowserTestMac + : public ExtensionMessageBubbleBrowserTest { + public: + ExtensionMessageBubbleBrowserTestMac() {} + ~ExtensionMessageBubbleBrowserTestMac() override {} + + private: + void SetUpCommandLine(base::CommandLine* command_line) override; + void CheckBubble(Browser* browser, AnchorPosition anchor) override; + void CloseBubble(Browser* browser) override; + + DISALLOW_COPY_AND_ASSIGN(ExtensionMessageBubbleBrowserTestMac); +}; + +void ExtensionMessageBubbleBrowserTestMac::SetUpCommandLine( + base::CommandLine* command_line) { + ExtensionMessageBubbleBrowserTest::SetUpCommandLine(command_line); + [ToolbarActionsBarBubbleMac setAnimationEnabledForTesting:NO]; +} + +void ExtensionMessageBubbleBrowserTestMac::CheckBubble( + Browser* browser, + AnchorPosition anchor) { + ToolbarController* toolbarController = ToolbarControllerForBrowser(browser); + BrowserActionsController* actionsController = + [toolbarController browserActionsController]; + NSView* anchorView = nil; + ToolbarActionsBarBubbleMac* bubble = [actionsController activeBubble]; + switch (anchor) { + case ANCHOR_BROWSER_ACTION: + anchorView = [actionsController buttonWithIndex:0]; + break; + case ANCHOR_WRENCH_MENU: + anchorView = [toolbarController wrenchButton]; + break; + } + CheckBubbleAndReferenceView(bubble, anchorView); +} + +void ExtensionMessageBubbleBrowserTestMac::CloseBubble(Browser* browser) { + BrowserActionsController* controller = + [ToolbarControllerForBrowser(browser) browserActionsController]; + ToolbarActionsBarBubbleMac* bubble = [controller activeBubble]; + ASSERT_TRUE(bubble); + [bubble close]; + EXPECT_EQ(nil, [controller activeBubble]); +} + +IN_PROC_BROWSER_TEST_F(ExtensionMessageBubbleBrowserTestMac, + ExtensionBubbleAnchoredToExtensionAction) { + TestBubbleAnchoredToExtensionAction(); +} + +IN_PROC_BROWSER_TEST_F(ExtensionMessageBubbleBrowserTestMac, + ExtensionBubbleAnchoredToWrenchMenu) { + TestBubbleAnchoredToWrenchMenu(); +} + +IN_PROC_BROWSER_TEST_F(ExtensionMessageBubbleBrowserTestMac, + PRE_ExtensionBubbleShowsOnStartup) { + PreBubbleShowsOnStartup(); +} + +IN_PROC_BROWSER_TEST_F(ExtensionMessageBubbleBrowserTestMac, + ExtensionBubbleShowsOnStartup) { + TestBubbleShowsOnStartup(); +}
diff --git a/chrome/browser/ui/cocoa/extensions/toolbar_actions_bar_bubble_mac.h b/chrome/browser/ui/cocoa/extensions/toolbar_actions_bar_bubble_mac.h index eef920d..81808e9 100644 --- a/chrome/browser/ui/cocoa/extensions/toolbar_actions_bar_bubble_mac.h +++ b/chrome/browser/ui/cocoa/extensions/toolbar_actions_bar_bubble_mac.h
@@ -39,6 +39,9 @@ delegate:(scoped_ptr<ToolbarActionsBarBubbleDelegate>) delegate; +// Toggles animation for testing purposes. ++ (void)setAnimationEnabledForTesting:(BOOL)enabled; + @property(readonly, nonatomic) NSButton* actionButton; @property(readonly, nonatomic) NSButton* dismissButton; @property(readonly, nonatomic) NSButton* learnMoreButton;
diff --git a/chrome/browser/ui/cocoa/extensions/toolbar_actions_bar_bubble_mac.mm b/chrome/browser/ui/cocoa/extensions/toolbar_actions_bar_bubble_mac.mm index e131d23..940d26b 100644 --- a/chrome/browser/ui/cocoa/extensions/toolbar_actions_bar_bubble_mac.mm +++ b/chrome/browser/ui/cocoa/extensions/toolbar_actions_bar_bubble_mac.mm
@@ -17,6 +17,10 @@ #import "ui/base/cocoa/window_size_constants.h" #include "ui/native_theme/native_theme.h" +namespace { +BOOL g_animations_enabled = false; +} + @class ExtensionMessageBubbleButton; @interface ToolbarActionsBarBubbleMac () @@ -91,11 +95,18 @@ gfx::SkColorToCalibratedNSColor(nativeTheme->GetSystemColor( ui::NativeTheme::kColorId_DialogBackground))]; + if (!g_animations_enabled) + [window setAllowedAnimations:info_bubble::kAnimateNone]; + [self layout]; } return self; } ++ (void)setAnimationEnabledForTesting:(BOOL)enabled { + g_animations_enabled = enabled; +} + - (IBAction)showWindow:(id)sender { delegate_->OnBubbleShown(); [super showWindow:sender];
diff --git a/chrome/browser/ui/cocoa/extensions/toolbar_actions_bar_bubble_mac_unittest.mm b/chrome/browser/ui/cocoa/extensions/toolbar_actions_bar_bubble_mac_unittest.mm index 2fcd80d..bca8680 100644 --- a/chrome/browser/ui/cocoa/extensions/toolbar_actions_bar_bubble_mac_unittest.mm +++ b/chrome/browser/ui/cocoa/extensions/toolbar_actions_bar_bubble_mac_unittest.mm
@@ -8,7 +8,6 @@ #include "base/strings/utf_string_conversions.h" #import "chrome/browser/ui/cocoa/cocoa_test_helper.h" #import "chrome/browser/ui/cocoa/extensions/toolbar_actions_bar_bubble_mac.h" -#import "chrome/browser/ui/cocoa/info_bubble_window.h" #import "chrome/browser/ui/cocoa/run_loop_testing.h" #include "chrome/browser/ui/toolbar/test_toolbar_actions_bar_bubble_delegate.h" #import "ui/events/test/cocoa_test_event_utils.h" @@ -59,6 +58,8 @@ ToolbarActionsBarBubbleMacTest() {} ~ToolbarActionsBarBubbleMacTest() override {} + void SetUp() override; + // Create and display a new bubble with the given |delegate|. ToolbarActionsBarBubbleMac* CreateAndShowBubble( TestToolbarActionsBarBubbleDelegate* delegate); @@ -78,6 +79,11 @@ DISALLOW_COPY_AND_ASSIGN(ToolbarActionsBarBubbleMacTest); }; +void ToolbarActionsBarBubbleMacTest::SetUp() { + CocoaTest::SetUp(); + [ToolbarActionsBarBubbleMac setAnimationEnabledForTesting:NO]; +} + ToolbarActionsBarBubbleMac* ToolbarActionsBarBubbleMacTest::CreateAndShowBubble( TestToolbarActionsBarBubbleDelegate* delegate) { ToolbarActionsBarBubbleMac* bubble = @@ -87,8 +93,6 @@ delegate:delegate->GetDelegate()]; EXPECT_FALSE(delegate->shown()); [bubble showWindow:nil]; - [base::mac::ObjCCastStrict<InfoBubbleWindow>([bubble window]) - setAllowedAnimations:info_bubble::kAnimateNone]; chrome::testing::NSRunLoopRunAllPending(); EXPECT_FALSE(delegate->close_action()); EXPECT_TRUE(delegate->shown());
diff --git a/chrome/browser/ui/cocoa/omnibox/omnibox_view_mac.mm b/chrome/browser/ui/cocoa/omnibox/omnibox_view_mac.mm index 7d633768..66a1bc2 100644 --- a/chrome/browser/ui/cocoa/omnibox/omnibox_view_mac.mm +++ b/chrome/browser/ui/cocoa/omnibox/omnibox_view_mac.mm
@@ -392,7 +392,7 @@ } model()->StartAutocomplete([editor selectedRange].length != 0, - prevent_inline_autocomplete); + prevent_inline_autocomplete, false); } void OmniboxViewMac::CloseOmniboxPopup() { @@ -736,7 +736,7 @@ if (cmd == @selector(insertBacktab:)) { if (model()->popup_model()->selected_line_state() == OmniboxPopupModel::KEYWORD) { - model()->ClearKeyword(GetText()); + model()->ClearKeyword(); return true; } else { model()->OnUpOrDownKeyPressed(-1); @@ -987,7 +987,7 @@ // We're showing a keyword and the user pressed backspace at the // beginning of the text. Delete the selected keyword. - model()->ClearKeyword(GetText()); + model()->ClearKeyword(); return true; }
diff --git a/chrome/browser/ui/cocoa/simple_message_box_mac.mm b/chrome/browser/ui/cocoa/simple_message_box_mac.mm index 767c963..732569fc 100644 --- a/chrome/browser/ui/cocoa/simple_message_box_mac.mm +++ b/chrome/browser/ui/cocoa/simple_message_box_mac.mm
@@ -25,7 +25,7 @@ // Ignore the title; it's the window title on other platforms and ignorable. NSAlert* alert = [[[NSAlert alloc] init] autorelease]; [alert setMessageText:base::SysUTF16ToNSString(message)]; - NSUInteger style = (type == MESSAGE_BOX_TYPE_INFORMATION) ? + NSAlertStyle style = (type == MESSAGE_BOX_TYPE_INFORMATION) ? NSInformationalAlertStyle : NSWarningAlertStyle; [alert setAlertStyle:style]; if (type == MESSAGE_BOX_TYPE_QUESTION) {
diff --git a/chrome/browser/ui/cocoa/web_dialog_window_controller.mm b/chrome/browser/ui/cocoa/web_dialog_window_controller.mm index b66c86c..87f40074 100644 --- a/chrome/browser/ui/cocoa/web_dialog_window_controller.mm +++ b/chrome/browser/ui/cocoa/web_dialog_window_controller.mm
@@ -199,8 +199,7 @@ void WebDialogWindowDelegateBridge::OnCloseContents(WebContents* source, bool* out_close_dialog) { - if (out_close_dialog) - *out_close_dialog = true; + *out_close_dialog = true; } void WebDialogWindowDelegateBridge::CloseContents(WebContents* source) {
diff --git a/chrome/browser/ui/extensions/extension_message_bubble_browsertest.cc b/chrome/browser/ui/extensions/extension_message_bubble_browsertest.cc new file mode 100644 index 0000000..9bea9c6d --- /dev/null +++ b/chrome/browser/ui/extensions/extension_message_bubble_browsertest.cc
@@ -0,0 +1,71 @@ +// 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 "chrome/browser/ui/extensions/extension_message_bubble_browsertest.h" + +#include "base/run_loop.h" +#include "chrome/browser/extensions/extension_action_test_util.h" +#include "chrome/browser/extensions/extension_service.h" +#include "chrome/browser/ui/extensions/extension_message_bubble_factory.h" +#include "extensions/common/feature_switch.h" + +ExtensionMessageBubbleBrowserTest::ExtensionMessageBubbleBrowserTest() { +} + +ExtensionMessageBubbleBrowserTest::~ExtensionMessageBubbleBrowserTest() { +} + +void ExtensionMessageBubbleBrowserTest::SetUpCommandLine( + base::CommandLine* command_line) { + BrowserActionsBarBrowserTest::SetUpCommandLine(command_line); + // The dev mode warning bubble is an easy one to trigger, so we use that for + // our testing purposes. + dev_mode_bubble_override_.reset( + new extensions::FeatureSwitch::ScopedOverride( + extensions::FeatureSwitch::force_dev_mode_highlighting(), + true)); + ExtensionMessageBubbleFactory::set_enabled_for_tests(true); +} + +void ExtensionMessageBubbleBrowserTest::TestBubbleAnchoredToExtensionAction() { + scoped_refptr<const extensions::Extension> action_extension = + extensions::extension_action_test_util::CreateActionExtension( + "action_extension", + extensions::extension_action_test_util::BROWSER_ACTION, + extensions::Manifest::UNPACKED); + extension_service()->AddExtension(action_extension.get()); + + Browser* second_browser = new Browser( + Browser::CreateParams(profile(), browser()->host_desktop_type())); + base::RunLoop().RunUntilIdle(); + + CheckBubble(second_browser, ANCHOR_BROWSER_ACTION); + CloseBubble(second_browser); +} + +void ExtensionMessageBubbleBrowserTest::TestBubbleAnchoredToWrenchMenu() { + scoped_refptr<const extensions::Extension> no_action_extension = + extensions::extension_action_test_util::CreateActionExtension( + "action_extension", + extensions::extension_action_test_util::NO_ACTION, + extensions::Manifest::UNPACKED); + extension_service()->AddExtension(no_action_extension.get()); + + Browser* second_browser = new Browser( + Browser::CreateParams(profile(), browser()->host_desktop_type())); + base::RunLoop().RunUntilIdle(); + + CheckBubble(second_browser, ANCHOR_WRENCH_MENU); + CloseBubble(second_browser); +} + +void ExtensionMessageBubbleBrowserTest::PreBubbleShowsOnStartup() { + LoadExtension(test_data_dir_.AppendASCII("good_unpacked")); +} + +void ExtensionMessageBubbleBrowserTest::TestBubbleShowsOnStartup() { + base::RunLoop().RunUntilIdle(); + CheckBubble(browser(), ANCHOR_BROWSER_ACTION); + CloseBubble(browser()); +}
diff --git a/chrome/browser/ui/extensions/extension_message_bubble_browsertest.h b/chrome/browser/ui/extensions/extension_message_bubble_browsertest.h new file mode 100644 index 0000000..1552130f --- /dev/null +++ b/chrome/browser/ui/extensions/extension_message_bubble_browsertest.h
@@ -0,0 +1,57 @@ +// 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 CHROME_BROWSER_UI_EXTENSIONS_EXTENSION_MESSAGE_BUBBLE_BROWSERTEST_H_ +#define CHROME_BROWSER_UI_EXTENSIONS_EXTENSION_MESSAGE_BUBBLE_BROWSERTEST_H_ + +#include "base/macros.h" +#include "base/memory/scoped_ptr.h" +#include "chrome/browser/ui/toolbar/browser_actions_bar_browsertest.h" + +class ExtensionMessageBubbleBrowserTest + : public BrowserActionsBarBrowserTest { + protected: + enum AnchorPosition { + ANCHOR_BROWSER_ACTION, + ANCHOR_WRENCH_MENU, + }; + + ExtensionMessageBubbleBrowserTest(); + ~ExtensionMessageBubbleBrowserTest() override; + + // BrowserActionsBarBrowserTest: + void SetUpCommandLine(base::CommandLine* command_line) override; + + // Checks the position of the bubble present in the given |browser|, when the + // bubble should be anchored at the given |anchor|. + virtual void CheckBubble(Browser* browser, AnchorPosition anchor) = 0; + + // Closes the bubble present in the given |browser|. + virtual void CloseBubble(Browser* browser) = 0; + + // The following are essentially the different tests, but we can't define the + // tests in this file, since it relies on platform-specific implementation + // (the above virtual methods). + + // Tests that an extension bubble will be anchored to an extension action when + // there are extensions with actions. + void TestBubbleAnchoredToExtensionAction(); + + // Tests that an extension bubble will be anchored to the wrench menu when + // there aren't any extensions with actions. + // This also tests that the crashes in crbug.com/476426 are fixed. + void TestBubbleAnchoredToWrenchMenu(); + + // Tests that the extension bubble will show on startup. + void PreBubbleShowsOnStartup(); + void TestBubbleShowsOnStartup(); + + private: + scoped_ptr<extensions::FeatureSwitch::ScopedOverride> + dev_mode_bubble_override_; + + DISALLOW_COPY_AND_ASSIGN(ExtensionMessageBubbleBrowserTest); +}; + +#endif // CHROME_BROWSER_UI_EXTENSIONS_EXTENSION_MESSAGE_BUBBLE_BROWSERTEST_H_
diff --git a/chrome/browser/ui/omnibox/omnibox_edit_model.cc b/chrome/browser/ui/omnibox/omnibox_edit_model.cc index dcbdd8a..6bc5f55 100644 --- a/chrome/browser/ui/omnibox/omnibox_edit_model.cc +++ b/chrome/browser/ui/omnibox/omnibox_edit_model.cc
@@ -560,7 +560,8 @@ void OmniboxEditModel::StartAutocomplete( bool has_selected_text, - bool prevent_inline_autocomplete) { + bool prevent_inline_autocomplete, + bool entering_keyword_mode) { // TODO(vadimt): Remove ScopedTracker below once crbug.com/440919 is fixed. tracked_objects::ScopedTracker tracking_profile( FROM_HERE_WITH_EXPLICIT_FUNCTION( @@ -570,12 +571,17 @@ // Cursor position is equivalent to the current selection's end. size_t start; view_->GetSelectionBounds(&start, &cursor_position); - // Adjust cursor position taking into account possible keyword in the user - // text. We rely on DisplayTextFromUserText() method which is consistent - // with keyword extraction done in KeywordProvider/SearchProvider. - const size_t cursor_offset = - user_text_.length() - DisplayTextFromUserText(user_text_).length(); - cursor_position += cursor_offset; + // If we're in keyword mode, we're not displaying the full |user_text_|, so + // the cursor position we got from the view has to be adjusted later by the + // length of the undisplayed text. If we're just entering keyword mode, + // though, we have to avoid making this adjustment, because we haven't + // actually hidden any text yet, but the caller has already cleared + // |is_keyword_hint_|, so DisplayTextFromUserText() will believe we are + // already in keyword mode, and will thus mis-adjust the cursor position. + if (!entering_keyword_mode) { + cursor_position += + user_text_.length() - DisplayTextFromUserText(user_text_).length(); + } } else { // There are some cases where StartAutocomplete() may be called // with non-empty |inline_autocomplete_text_|. In such cases, we cannot @@ -894,16 +900,40 @@ if (popup_model() && popup_model()->IsOpen()) popup_model()->SetSelectedLineState(OmniboxPopupModel::KEYWORD); else - StartAutocomplete(false, true); + StartAutocomplete(false, true, true); - // Ensure the current selection is saved before showing keyword mode - // so that moving to another line and then reverting the text will restore - // the current state properly. - bool save_original_selection = !has_temporary_text_; - has_temporary_text_ = true; - view_->OnTemporaryTextMaybeChanged( - DisplayTextFromUserText(CurrentMatch(NULL).fill_into_edit), - save_original_selection, true); + // When entering keyword mode via tab, the new text to show is whatever the + // newly-selected match in the dropdown is. When entering via space, however, + // we should make sure to use the actual |user_text_| as the basis for the new + // text. This ensures that if the user types "<keyword><space>" and the + // default match would have inline autocompleted a further string (e.g. + // because there's a past multi-word search beginning with this keyword), the + // inline autocompletion doesn't get filled in as the keyword search query + // text. + // + // We also treat tabbing into keyword mode like tabbing through the popup in + // that we set |has_temporary_text_|, whereas pressing space is treated like + // a new keystroke that changes the current match instead of overlaying it + // with a temporary one. This is important because rerunning autocomplete + // after the user pressed space, which will have happened just before reaching + // here, may have generated a new match, which the user won't actually see and + // which we don't want to switch back to when existing keyword mode; see + // comments in ClearKeyword(). + if (entered_method == ENTERED_KEYWORD_MODE_VIA_TAB) { + // Ensure the current selection is saved before showing keyword mode + // so that moving to another line and then reverting the text will restore + // the current state properly. + bool save_original_selection = !has_temporary_text_; + has_temporary_text_ = true; + const AutocompleteMatch& match = CurrentMatch(NULL); + original_url_ = match.destination_url; + view_->OnTemporaryTextMaybeChanged( + DisplayTextFromUserText(match.fill_into_edit), + save_original_selection, true); + } else { + view_->OnTemporaryTextMaybeChanged(DisplayTextFromUserText(user_text_), + false, true); + } view_->UpdatePlaceholderText(); @@ -922,29 +952,41 @@ delegate_->OnInputStateChanged(); } -void OmniboxEditModel::ClearKeyword(const base::string16& visible_text) { +void OmniboxEditModel::ClearKeyword() { autocomplete_controller()->Stop(false); omnibox_controller_->ClearPopupKeywordMode(); - const base::string16 window_text(keyword_ + visible_text); + const base::string16 window_text(keyword_ + view_->GetText()); - // Only reset the result if the edit text has changed since the - // keyword was accepted, or if the popup is closed. - if (just_deleted_text_ || !visible_text.empty() || - !(popup_model() && popup_model()->IsOpen())) { + // If we've tabbed into keyword mode and haven't done anything else, + // |has_temporary_text_| will be true, and we should just revert into keyword + // hint mode; otherwise we do a more complete state update/revert via + // OnBefore/AfterPossibleChange(). + // + // If we were to do the "complete state revert" all the time, then in cases + // where our associated keyword match is in the middle of the popup instead of + // on the first line, tabbing into it and then hitting shift-tab would reset + // the entire popup contents, and we'd wind up with the first line selected. + // + // If we were instead to "just switch back to keyword hint mode" all the time, + // we could wind up with strange state in some cases. For example, if a user + // has a keyword named "x", an inline-autocompletable history site "xyz.com", + // and a lower-ranked inline-autocompletable search "x y", then typing "x" + // will inline autocomplete to "xyz.com", hitting space will toggle into + // keyword mode, but then hitting backspace might wind up with the default + // match as the "x y" search, due to the particulars of how we re-run + // autocomplete for "x " before toggling into keyword mode to begin with. + if (has_temporary_text_) { + is_keyword_hint_ = true; + view_->SetWindowTextAndCaretPos(window_text.c_str(), keyword_.length(), + false, true); + } else { view_->OnBeforePossibleChange(); view_->SetWindowTextAndCaretPos(window_text.c_str(), keyword_.length(), false, false); keyword_.clear(); is_keyword_hint_ = false; view_->OnAfterPossibleChange(); - just_deleted_text_ = true; // OnAfterPossibleChange() fails to clear this - // since the edit contents have actually grown - // longer. - } else { - is_keyword_hint_ = true; - view_->SetWindowTextAndCaretPos(window_text.c_str(), keyword_.length(), - false, true); } view_->UpdatePlaceholderText(); @@ -1375,7 +1417,11 @@ // have gotten here. CHECK(!result().empty()); CHECK(popup_model()->selected_line() < result().size()); - *match = result().match_at(popup_model()->selected_line()); + const AutocompleteMatch& selected_match = + result().match_at(popup_model()->selected_line()); + *match = + (popup_model()->selected_line_state() == OmniboxPopupModel::KEYWORD) ? + *selected_match.associated_keyword : selected_match; } if (alternate_nav_url && (!popup_model() || popup_model()->manually_selected_match().empty())) @@ -1402,8 +1448,7 @@ bool OmniboxEditModel::MaybeAcceptKeywordBySpace( const base::string16& new_text) { size_t keyword_length = new_text.length() - 1; - return (paste_state_ == NONE) && is_keyword_hint_ && !keyword_.empty() && - inline_autocomplete_text_.empty() && + return (paste_state_ == NONE) && is_keyword_hint_ && (keyword_.length() == keyword_length) && IsSpaceCharForAcceptingKeyword(new_text[keyword_length]) && !new_text.compare(0, keyword_length, keyword_, 0, keyword_length) &&
diff --git a/chrome/browser/ui/omnibox/omnibox_edit_model.h b/chrome/browser/ui/omnibox/omnibox_edit_model.h index 274fca10..7260790 100644 --- a/chrome/browser/ui/omnibox/omnibox_edit_model.h +++ b/chrome/browser/ui/omnibox/omnibox_edit_model.h
@@ -174,7 +174,8 @@ // Directs the popup to start autocomplete. void StartAutocomplete(bool has_selected_text, - bool prevent_inline_autocomplete); + bool prevent_inline_autocomplete, + bool entering_keyword_mode); // Closes the popup and cancels any pending asynchronous queries. void StopAutocomplete(); @@ -242,9 +243,8 @@ // Accepts the current temporary text as the user text. void AcceptTemporaryTextAsUserText(); - // Clears the current keyword. |visible_text| is the (non-keyword) text - // currently visible in the edit. - void ClearKeyword(const base::string16& visible_text); + // Clears the current keyword. + void ClearKeyword(); // Returns the current autocomplete result. This logic should in the future // live in AutocompleteController but resides here for now. This method is
diff --git a/chrome/browser/ui/omnibox/omnibox_popup_model.cc b/chrome/browser/ui/omnibox/omnibox_popup_model.cc index c43b961..d20ee265 100644 --- a/chrome/browser/ui/omnibox/omnibox_popup_model.cc +++ b/chrome/browser/ui/omnibox/omnibox_popup_model.cc
@@ -64,6 +64,8 @@ if (!description_width) return; + // If we want to display the description, we need to reserve enough space for + // the separator. available_width -= separator_width; if (contents_width + description_width > available_width) {
diff --git a/chrome/browser/ui/omnibox/omnibox_popup_model.h b/chrome/browser/ui/omnibox/omnibox_popup_model.h index dc5f7344..c87302b 100644 --- a/chrome/browser/ui/omnibox/omnibox_popup_model.h +++ b/chrome/browser/ui/omnibox/omnibox_popup_model.h
@@ -87,8 +87,14 @@ void Move(int count); // If the selected line has both a normal match and a keyword match, this can - // be used to choose which to select. It is an error to call this when the - // selected line does not have both matches (or there is no selection). + // be used to choose which to select. This allows the user to toggle between + // normal and keyword mode with tab/shift-tab without rerunning autocomplete + // or disturbing other popup state, which in turn is an important part of + // supporting the use of tab to do both tab-to-search and + // tab-to-traverse-dropdown. + // + // It is an error to call this when the selected line does not have both + // matches (or there is no selection). void SetSelectedLineState(LineState state); // Called when the user hits shift-delete. This should determine if the item
diff --git a/chrome/browser/ui/omnibox/omnibox_view_browsertest.cc b/chrome/browser/ui/omnibox/omnibox_view_browsertest.cc index 11f8029..8c02bb4 100644 --- a/chrome/browser/ui/omnibox/omnibox_view_browsertest.cc +++ b/chrome/browser/ui/omnibox/omnibox_view_browsertest.cc
@@ -883,7 +883,7 @@ ASSERT_TRUE(omnibox_view->GetText().empty()); // Revert to keyword hint mode. - omnibox_view->model()->ClearKeyword(base::string16()); + omnibox_view->model()->ClearKeyword(); ASSERT_TRUE(omnibox_view->model()->is_keyword_hint()); ASSERT_EQ(search_keyword, omnibox_view->model()->keyword()); ASSERT_EQ(search_keyword, omnibox_view->GetText()); @@ -898,7 +898,7 @@ ASSERT_TRUE(omnibox_view->GetText().empty()); // Revert to keyword hint mode. - omnibox_view->model()->ClearKeyword(base::string16()); + omnibox_view->model()->ClearKeyword(); ASSERT_TRUE(omnibox_view->model()->is_keyword_hint()); ASSERT_EQ(search_keyword, omnibox_view->model()->keyword()); ASSERT_EQ(search_keyword, omnibox_view->GetText());
diff --git a/chrome/browser/ui/search/instant_search_prerenderer_unittest.cc b/chrome/browser/ui/search/instant_search_prerenderer_unittest.cc index 9987f46..a347a39 100644 --- a/chrome/browser/ui/search/instant_search_prerenderer_unittest.cc +++ b/chrome/browser/ui/search/instant_search_prerenderer_unittest.cc
@@ -83,8 +83,7 @@ Profile* profile, const GURL& url, const Referrer& referrer, - Origin origin, - uint8 experiment_id) override; + Origin origin) override; private: bool call_did_finish_load_; @@ -99,8 +98,7 @@ const Referrer& referrer, Origin origin, bool call_did_finish_load) - : PrerenderContents(prerender_manager, profile, url, referrer, origin, - PrerenderManager::kNoExperiment), + : PrerenderContents(prerender_manager, profile, url, referrer, origin), profile_(profile), url_(url), call_did_finish_load_(call_did_finish_load) { @@ -143,8 +141,7 @@ Profile* profile, const GURL& url, const Referrer& referrer, - Origin origin, - uint8 experiment_id) { + Origin origin) { return new DummyPrerenderContents(prerender_manager, profile, url, referrer, origin, call_did_finish_load_); }
diff --git a/chrome/browser/ui/sync/inline_login_dialog.cc b/chrome/browser/ui/sync/inline_login_dialog.cc index 555cdbb..dfd056d 100644 --- a/chrome/browser/ui/sync/inline_login_dialog.cc +++ b/chrome/browser/ui/sync/inline_login_dialog.cc
@@ -48,8 +48,7 @@ void InlineLoginDialog::OnCloseContents( content::WebContents* source, bool* out_close_dialog) { - if (out_close_dialog) - *out_close_dialog = true; + *out_close_dialog = true; } bool InlineLoginDialog::ShouldShowDialogTitle() const {
diff --git a/chrome/browser/ui/tab_helpers.cc b/chrome/browser/ui/tab_helpers.cc index df974a92d..fb3a6ce 100644 --- a/chrome/browser/ui/tab_helpers.cc +++ b/chrome/browser/ui/tab_helpers.cc
@@ -8,6 +8,8 @@ #include "chrome/browser/browser_process.h" #include "chrome/browser/content_settings/chrome_content_settings_client.h" #include "chrome/browser/content_settings/tab_specific_content_settings.h" +#include "chrome/browser/engagement/site_engagement_helper.h" +#include "chrome/browser/engagement/site_engagement_service.h" #include "chrome/browser/favicon/favicon_helper.h" #include "chrome/browser/history/history_tab_helper.h" #include "chrome/browser/infobars/infobar_service.h" @@ -152,6 +154,8 @@ PrefsTabHelper::CreateForWebContents(web_contents); prerender::PrerenderTabHelper::CreateForWebContents(web_contents); SearchTabHelper::CreateForWebContents(web_contents); + if (SiteEngagementService::IsEnabled()) + SiteEngagementHelper::CreateForWebContents(web_contents); // TODO(vabr): Remove TabSpecificContentSettings from here once their function // is taken over by ChromeContentSettingsClient. http://crbug.com/387075 TabSpecificContentSettings::CreateForWebContents(web_contents);
diff --git a/chrome/browser/ui/toolbar/browser_actions_bar_browsertest.cc b/chrome/browser/ui/toolbar/browser_actions_bar_browsertest.cc index 04de2ffd..1308607 100644 --- a/chrome/browser/ui/toolbar/browser_actions_bar_browsertest.cc +++ b/chrome/browser/ui/toolbar/browser_actions_bar_browsertest.cc
@@ -172,10 +172,8 @@ EXPECT_EQ(3, browser_actions_bar()->VisibleBrowserActions()); EXPECT_EQ(extension_a()->id(), browser_actions_bar()->GetExtensionId(0)); // Force hide one of the extensions' browser action. - extensions::ExtensionActionAPI::SetBrowserActionVisibility( - extensions::ExtensionPrefs::Get(browser()->profile()), - extension_a()->id(), - false); + extensions::ExtensionActionAPI::Get(browser()->profile())-> + SetBrowserActionVisibility(extension_a()->id(), false); // The browser action for Extension A should be removed. EXPECT_EQ(2, browser_actions_bar()->VisibleBrowserActions()); EXPECT_EQ(extension_b()->id(), browser_actions_bar()->GetExtensionId(0));
diff --git a/chrome/browser/ui/toolbar/toolbar_actions_bar.cc b/chrome/browser/ui/toolbar/toolbar_actions_bar.cc index 72e570e..a8cf716 100644 --- a/chrome/browser/ui/toolbar/toolbar_actions_bar.cc +++ b/chrome/browser/ui/toolbar/toolbar_actions_bar.cc
@@ -16,7 +16,6 @@ #include "chrome/browser/ui/extensions/extension_action_view_controller.h" #include "chrome/browser/ui/extensions/extension_message_bubble_factory.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" -#include "chrome/browser/ui/tabs/tab_strip_model_observer.h" #include "chrome/browser/ui/toolbar/component_toolbar_actions_factory.h" #include "chrome/browser/ui/toolbar/toolbar_action_view_controller.h" #include "chrome/browser/ui/toolbar/toolbar_actions_bar_delegate.h" @@ -101,305 +100,8 @@ bool ToolbarActionsBar::disable_animations_for_testing_ = false; // static -bool ToolbarActionsBar::pop_out_actions_to_run_ = false; - -// static bool ToolbarActionsBar::send_overflowed_action_changes_ = true; -// A class to implement an optional tab ordering that "pops out" actions that -// want to run on a particular page, as part of our experimentation with how to -// best signal that actions like extension page actions want to run. -// TODO(devlin): Once we finally settle on the right behavior, determine if -// we need this. -class ToolbarActionsBar::TabOrderHelper - : public TabStripModelObserver { - public: - TabOrderHelper(ToolbarActionsBar* toolbar, - Browser* browser, - extensions::ExtensionToolbarModel* model); - ~TabOrderHelper() override; - - // Returns the number of extra icons that should appear on the given |tab_id| - // because of actions that are going to pop out. - size_t GetExtraIconCount(int tab_id); - - // Returns the item order of actions for the tab with the given - // |web_contents|. - WeakToolbarActions GetActionOrder(content::WebContents* web_contents); - - // Notifies the TabOrderHelper that a given |action| does/doesn't want to run - // on the tab indicated by |tab_id|. - void SetActionWantsToRun(ToolbarActionViewController* action, - int tab_id, - bool wants_to_run); - - // Notifies the TabOrderHelper that a given |action| has been removed. - void ActionRemoved(ToolbarActionViewController* action); - - // Handles a resize, including updating any actions that want to act and - // updating the model. - void HandleResize(size_t resized_count, int tab_id); - - // Handles a drag and drop, including updating any actions that want to act - // and updating the model. - void HandleDragDrop(int dragged, - int dropped, - ToolbarActionsBar::DragType drag_type, - int tab_id); - - void notify_overflow_bar(ToolbarActionsBar* overflow_bar, - bool should_notify) { - // There's a possibility that a new overflow bar can be constructed before - // the first is fully destroyed. Only un-register the |overflow_bar_| if - // it's the one making the request. - if (should_notify) - overflow_bar_ = overflow_bar; - else if (overflow_bar_ == overflow_bar) - overflow_bar_ = nullptr; - } - - private: - // TabStripModelObserver: - void TabInsertedAt(content::WebContents* web_contents, - int index, - bool foreground) override; - void TabDetachedAt(content::WebContents* web_contents, int index) override; - void ActiveTabChanged(content::WebContents* old_contents, - content::WebContents* new_contents, - int index, - int reason) override; - void TabStripModelDeleted() override; - - // Notifies the main |toolbar_| and, if present, the |overflow_bar_| that - // actions need to be reordered. - void NotifyReorderActions(); - - // The set of tabs for the given action (the key) is currently "popped out". - // "Popped out" actions are those that were in the overflow menu normally, but - // want to run and are moved to the main bar so the user can see them. - std::map<ToolbarActionViewController*, std::set<int>> popped_out_in_tabs_; - - // The set of tab ids that have been checked for whether actions need to be - // popped out or not. - std::set<int> tabs_checked_for_pop_out_; - - // The owning ToolbarActionsBar. - ToolbarActionsBar* toolbar_; - - // The overflow bar, if one is present. - ToolbarActionsBar* overflow_bar_; - - // The associated toolbar model. - extensions::ExtensionToolbarModel* model_; - - // A scoped tab strip observer so we can clean up |tabs_checked_for_popout_|. - ScopedObserver<TabStripModel, TabStripModelObserver> tab_strip_observer_; - - DISALLOW_COPY_AND_ASSIGN(TabOrderHelper); -}; - -ToolbarActionsBar::TabOrderHelper::TabOrderHelper( - ToolbarActionsBar* toolbar, - Browser* browser, - extensions::ExtensionToolbarModel* model) - : toolbar_(toolbar), - overflow_bar_(nullptr), - model_(model), - tab_strip_observer_(this) { - tab_strip_observer_.Add(browser->tab_strip_model()); -} - -ToolbarActionsBar::TabOrderHelper::~TabOrderHelper() { -} - -size_t ToolbarActionsBar::TabOrderHelper::GetExtraIconCount(int tab_id) { - size_t extra_icons = 0; - const WeakToolbarActions& toolbar_actions = toolbar_->toolbar_actions(); - for (ToolbarActionViewController* action : toolbar_actions) { - auto actions_tabs = popped_out_in_tabs_.find(action); - if (actions_tabs != popped_out_in_tabs_.end() && - actions_tabs->second.count(tab_id)) - ++extra_icons; - } - return extra_icons; -} - -void ToolbarActionsBar::TabOrderHelper::HandleResize(size_t resized_count, - int tab_id) { - int extra = GetExtraIconCount(tab_id); - size_t tab_icon_count = model_->visible_icon_count() + extra; - bool reorder_necessary = false; - const WeakToolbarActions& toolbar_actions = toolbar_->toolbar_actions(); - if (resized_count < tab_icon_count) { - for (int i = resized_count; i < extra; ++i) { - // If an extension that was popped out to act is overflowed, then it - // should no longer be popped out, and it also doesn't count for adjusting - // the visible count (since it wasn't really out to begin with). - if (popped_out_in_tabs_[toolbar_actions[i]].count(tab_id)) { - reorder_necessary = true; - popped_out_in_tabs_[toolbar_actions[i]].erase(tab_id); - ++(resized_count); - } - } - } else { - // If the user increases the toolbar size while actions that popped out are - // visible, we need to re-arrange the icons in other windows to be - // consistent with what the user sees. - // That is, if the normal order is A, B, [C, D] (with C and D hidden), C - // pops out to act, and then the user increases the size of the toolbar, - // the user sees uncovering D (since C is already out). This is what should - // happen in all windows. - for (size_t i = tab_icon_count; i < resized_count; ++i) { - if (toolbar_actions[i]->GetId() != - model_->toolbar_items()[i - extra]->id()) - model_->MoveExtensionIcon(toolbar_actions[i]->GetId(), i - extra); - } - } - - resized_count -= extra; - model_->SetVisibleIconCount(resized_count); - if (reorder_necessary) - NotifyReorderActions(); -} - -void ToolbarActionsBar::TabOrderHelper::HandleDragDrop( - int dragged_index, - int dropped_index, - ToolbarActionsBar::DragType drag_type, - int tab_id) { - const WeakToolbarActions& toolbar_actions = toolbar_->toolbar_actions(); - ToolbarActionViewController* action = toolbar_actions[dragged_index]; - int delta = 0; - switch (drag_type) { - case ToolbarActionsBar::DRAG_TO_OVERFLOW: - // If the user moves an action back into overflow, then we don't adjust - // the base visible count, but do stop popping that action out. - if (popped_out_in_tabs_[action].count(tab_id)) - popped_out_in_tabs_[action].erase(tab_id); - else - delta = -1; - break; - case ToolbarActionsBar::DRAG_TO_MAIN: - delta = 1; - break; - case ToolbarActionsBar::DRAG_TO_SAME: - // If the user moves an action that had popped out to be on the toolbar, - // then we treat it as "pinning" the action, and adjust the base visible - // count to accommodate. - if (popped_out_in_tabs_[action].count(tab_id)) { - delta = 1; - popped_out_in_tabs_[action].erase(tab_id); - } - break; - } - - // If there are any actions that are in front of the dropped index only - // because they were popped out, decrement the dropped index. - for (int i = 0; i < dropped_index; ++i) { - if (i != dragged_index && - model_->GetIndexForId(toolbar_actions[i]->GetId()) >= dropped_index) - --dropped_index; - } - - model_->MoveExtensionIcon(action->GetId(), dropped_index); - - if (delta) - model_->SetVisibleIconCount(model_->visible_icon_count() + delta); -} - -void ToolbarActionsBar::TabOrderHelper::SetActionWantsToRun( - ToolbarActionViewController* action, - int tab_id, - bool wants_to_run) { - bool is_overflowed = model_->GetIndexForId(action->GetId()) >= - static_cast<int>(model_->visible_icon_count()); - bool reorder_necessary = false; - if (wants_to_run && is_overflowed) { - popped_out_in_tabs_[action].insert(tab_id); - reorder_necessary = true; - } else if (!wants_to_run && popped_out_in_tabs_[action].count(tab_id)) { - popped_out_in_tabs_[action].erase(tab_id); - reorder_necessary = true; - } - if (reorder_necessary) - NotifyReorderActions(); -} - -void ToolbarActionsBar::TabOrderHelper::ActionRemoved( - ToolbarActionViewController* action) { - popped_out_in_tabs_.erase(action); -} - -WeakToolbarActions ToolbarActionsBar::TabOrderHelper::GetActionOrder( - content::WebContents* web_contents) { - WeakToolbarActions toolbar_actions = toolbar_->toolbar_actions(); - // First, make sure that we've checked any actions that want to run. - int tab_id = SessionTabHelper::IdForTab(web_contents); - if (!tabs_checked_for_pop_out_.count(tab_id)) { - tabs_checked_for_pop_out_.insert(tab_id); - for (ToolbarActionViewController* toolbar_action : toolbar_actions) { - if (toolbar_action->WantsToRun(web_contents)) - popped_out_in_tabs_[toolbar_action].insert(tab_id); - } - } - - // Then, shift any actions that want to run to the front. - size_t insert_at = 0; - // Rotate any actions that want to run to the boundary between visible and - // overflowed actions. - for (WeakToolbarActions::iterator iter = - toolbar_actions.begin() + model_->visible_icon_count(); - iter != toolbar_actions.end(); ++iter) { - if (popped_out_in_tabs_[(*iter)].count(tab_id)) { - std::rotate(toolbar_actions.begin() + insert_at, iter, iter + 1); - ++insert_at; - } - } - - return toolbar_actions; -} - -void ToolbarActionsBar::TabOrderHelper::TabInsertedAt( - content::WebContents* web_contents, - int index, - bool foreground) { - if (foreground) - NotifyReorderActions(); -} - -void ToolbarActionsBar::TabOrderHelper::TabDetachedAt( - content::WebContents* web_contents, - int index) { - int tab_id = SessionTabHelper::IdForTab(web_contents); - for (auto& tabs : popped_out_in_tabs_) - tabs.second.erase(tab_id); - tabs_checked_for_pop_out_.erase(tab_id); -} - -void ToolbarActionsBar::TabOrderHelper::ActiveTabChanged( - content::WebContents* old_contents, - content::WebContents* new_contents, - int index, - int reason) { - // When we do a bulk-refresh by switching tabs, we don't animate the - // difference. We only animate when it's a change driven by the action or the - // user. - base::AutoReset<bool> animation_reset(&toolbar_->suppress_animation_, true); - NotifyReorderActions(); -} - -void ToolbarActionsBar::TabOrderHelper::TabStripModelDeleted() { - tab_strip_observer_.RemoveAll(); -} - -void ToolbarActionsBar::TabOrderHelper::NotifyReorderActions() { - // Reorder the reference toolbar first (since we use its actions in - // GetActionOrder()). - toolbar_->ReorderActions(); - if (overflow_bar_) - overflow_bar_->ReorderActions(); -} - ToolbarActionsBar::PlatformSettings::PlatformSettings(bool in_overflow_mode) : left_padding(in_overflow_mode ? kOverflowLeftPadding : kLeftPadding), right_padding(in_overflow_mode ? kOverflowRightPadding : kRightPadding), @@ -425,13 +127,6 @@ weak_ptr_factory_(this) { if (model_) // |model_| can be null in unittests. model_observer_.Add(model_); - - if (pop_out_actions_to_run_) { - if (in_overflow_mode()) - main_bar_->tab_order_helper_->notify_overflow_bar(this, true); - else - tab_order_helper_.reset(new TabOrderHelper(this, browser_, model_)); - } } ToolbarActionsBar::~ToolbarActionsBar() { @@ -439,8 +134,6 @@ // the order of deletion between the views and the ToolbarActionsBar. DCHECK(toolbar_actions_.empty()) << "Must call DeleteActions() before destruction."; - if (in_overflow_mode() && pop_out_actions_to_run_) - main_bar_->tab_order_helper_->notify_overflow_bar(this, false); } // static @@ -536,16 +229,9 @@ if (!model_) return 0u; - size_t extra_icons = 0; - if (tab_order_helper_) { - extra_icons = tab_order_helper_->GetExtraIconCount( - SessionTabHelper::IdForTab( - browser_->tab_strip_model()->GetActiveWebContents())); - } - size_t visible_icons = in_overflow_mode() ? toolbar_actions_.size() - main_bar_->GetIconCount() : - model_->visible_icon_count() + extra_icons; + model_->visible_icon_count(); #if DCHECK_IS_ON() // Good time for some sanity checks: We should never try to display more @@ -684,13 +370,7 @@ // Save off the desired number of visible icons. We do this now instead of // at the end of the animation so that even if the browser is shut down // while animating, the right value will be restored on next run. - if (tab_order_helper_) { - tab_order_helper_->HandleResize( - resized_count, - SessionTabHelper::IdForTab(GetCurrentWebContents())); - } else { - model_->SetVisibleIconCount(resized_count); - } + model_->SetVisibleIconCount(resized_count); } void ToolbarActionsBar::OnDragDrop(int dragged_index, @@ -702,23 +382,15 @@ return; } - if (tab_order_helper_) { - tab_order_helper_->HandleDragDrop( - dragged_index, - dropped_index, - drag_type, - SessionTabHelper::IdForTab(GetCurrentWebContents())); - } else { - int delta = 0; - if (drag_type == DRAG_TO_OVERFLOW) - delta = -1; - else if (drag_type == DRAG_TO_MAIN) - delta = 1; - model_->MoveExtensionIcon(toolbar_actions_[dragged_index]->GetId(), - dropped_index); - if (delta) - model_->SetVisibleIconCount(model_->visible_icon_count() + delta); - } + int delta = 0; + if (drag_type == DRAG_TO_OVERFLOW) + delta = -1; + else if (drag_type == DRAG_TO_MAIN) + delta = 1; + model_->MoveExtensionIcon(toolbar_actions_[dragged_index]->GetId(), + dropped_index); + if (delta) + model_->SetVisibleIconCount(model_->visible_icon_count() + delta); } void ToolbarActionsBar::MaybeShowExtensionBubble() { @@ -776,8 +448,6 @@ scoped_ptr<ToolbarActionViewController> removed_action(*iter); toolbar_actions_.weak_erase(iter); delegate_->RemoveViewForAction(removed_action.get()); - if (tab_order_helper_) - tab_order_helper_->ActionRemoved(removed_action.get()); removed_action.reset(); // If the extension is being upgraded we don't want the bar to shrink @@ -820,18 +490,9 @@ // There might not be a view in cases where we are highlighting or if we // haven't fully initialized the actions. if (action) { - content::WebContents* web_contents = GetCurrentWebContents(); action->UpdateState(); - - if (tab_order_helper_) { - tab_order_helper_->SetActionWantsToRun( - action, - SessionTabHelper::IdForTab(web_contents), - action->WantsToRun(web_contents)); - } + SetOverflowedActionWantsToRun(); } - - SetOverflowedActionWantsToRun(); } bool ToolbarActionsBar::ShowExtensionActionPopup( @@ -910,21 +571,6 @@ }; SortContainer(&toolbar_actions_.get(), model_->toolbar_items(), compare); - // Only adjust the order if the model isn't highlighting a particular - // subset (and the specialized tab order is enabled). - TabOrderHelper* tab_order_helper = in_overflow_mode() ? - main_bar_->tab_order_helper_.get() : tab_order_helper_.get(); - if (!model_->is_highlighting() && tab_order_helper) { - WeakToolbarActions new_order = - tab_order_helper->GetActionOrder(GetCurrentWebContents()); - auto compare = [](ToolbarActionViewController* const& first, - ToolbarActionViewController* const& second) { - return first->GetId() == second->GetId(); - }; - SortContainer( - &toolbar_actions_.get(), new_order, compare); - } - // Our visible browser actions may have changed - re-Layout() and check the // size (if we aren't suppressing the layout). if (!suppress_layout_) {
diff --git a/chrome/browser/ui/toolbar/toolbar_actions_bar.h b/chrome/browser/ui/toolbar/toolbar_actions_bar.h index e5d974b..6cdba0d 100644 --- a/chrome/browser/ui/toolbar/toolbar_actions_bar.h +++ b/chrome/browser/ui/toolbar/toolbar_actions_bar.h
@@ -137,11 +137,6 @@ ToolbarActionsBarDelegate* delegate_for_test() { return delegate_; } - static void set_pop_out_actions_to_run_for_testing( - bool pop_out_actions_to_run) { - pop_out_actions_to_run_ = pop_out_actions_to_run; - } - static void set_send_overflowed_action_changes_for_testing( bool send_overflowed_action_changes) { send_overflowed_action_changes_ = send_overflowed_action_changes; @@ -153,8 +148,6 @@ static bool disable_animations_for_testing_; private: - class TabOrderHelper; - using ToolbarActions = ScopedVector<ToolbarActionViewController>; // ExtensionToolbarModel::Observer: @@ -215,10 +208,6 @@ // The toolbar actions. ToolbarActions toolbar_actions_; - // The TabOrderHelper that manages popping out actions that want to act. - // This is only non-null if |pop_out_actions_to_run| is true. - scoped_ptr<TabOrderHelper> tab_order_helper_; - ScopedObserver<extensions::ExtensionToolbarModel, extensions::ExtensionToolbarModel::Observer> model_observer_;
diff --git a/chrome/browser/ui/toolbar/toolbar_actions_bar_unittest.cc b/chrome/browser/ui/toolbar/toolbar_actions_bar_unittest.cc index 2d21db0..e583536 100644 --- a/chrome/browser/ui/toolbar/toolbar_actions_bar_unittest.cc +++ b/chrome/browser/ui/toolbar/toolbar_actions_bar_unittest.cc
@@ -480,303 +480,3 @@ EXPECT_TRUE( prefs->GetBoolean(prefs::kToolbarIconSurfacingBubbleAcknowledged)); } - -class ToolbarActionsBarPopOutUnitTest - : public ToolbarActionsBarRedesignUnitTest { - public: - ToolbarActionsBarPopOutUnitTest() {} - - protected: - void SetUp() override { - ToolbarActionsBar::set_pop_out_actions_to_run_for_testing(true); - ToolbarActionsBarUnitTest::SetUp(); - } - - private: - DISALLOW_COPY_AND_ASSIGN(ToolbarActionsBarPopOutUnitTest); -}; - -// Test that toolbar actions can pop themselves out of overflow if they want to -// act on a given tab. -TEST_F(ToolbarActionsBarPopOutUnitTest, ActionsPopOutToAct) { - // Add three extensions to the profile; this is the easiest way to have - // toolbar actions. - const char kBrowserAction[] = "browser action"; - const char kPageAction[] = "page action"; - const char kSynthetic[] = "synthetic"; // This has a generated action icon. - - CreateAndAddExtension(kBrowserAction, - extensions::extension_action_test_util::BROWSER_ACTION); - scoped_refptr<const extensions::Extension> page_action = - CreateAndAddExtension( - kPageAction, extensions::extension_action_test_util::PAGE_ACTION); - CreateAndAddExtension(kSynthetic, - extensions::extension_action_test_util::NO_ACTION); - - { - // We should start in the order of "browser action", "page action", - // "synthetic" and have all actions visible. - const char* expected_names[] = { kBrowserAction, kPageAction, kSynthetic }; - EXPECT_TRUE(VerifyToolbarOrder(expected_names, 3u, 3u)); - } - - // Shrink the bar to only show one action, and move the page action to the - // end. - toolbar_model()->SetVisibleIconCount(1); - toolbar_model()->MoveExtensionIcon(page_action->id(), 2u); - - { - // Quickly verify that the move/visible count worked. - const char* expected_names[] = { kBrowserAction, kSynthetic, kPageAction }; - EXPECT_TRUE(VerifyToolbarOrder(expected_names, 3u, 1u)); - } - - // Create two tabs. - AddTab(browser(), GURL("http://www.google.com/")); - AddTab(browser(), GURL("http://www.youtube.com/")); - content::WebContents* web_contents = - browser()->tab_strip_model()->GetWebContentsAt(0); - - { - // First, check the order for the first tab. Since we haven't changed - // anything (i.e., no extensions want to act), this should be the same as we - // left it: "browser action", "synthetic", "page action", with only one - // visible. - ActivateTab(0); - const char* expected_names[] = { kBrowserAction, kSynthetic, kPageAction }; - EXPECT_TRUE(VerifyToolbarOrder(expected_names, 3u, 1u)); - } - - extensions::ExtensionActionManager* action_manager = - extensions::ExtensionActionManager::Get(profile()); - ExtensionAction* action = action_manager->GetExtensionAction(*page_action); - ASSERT_TRUE(action); - - { - // Make "page action" want to act. - SetActionWantsToRunOnTab(action, web_contents, true); - // This should result in "page action" being popped out of the overflow - // menu. - // This has two visible effects: - // - page action should moved to the zero-index (left-most side of the bar). - // - The visible count should increase by one (so page action is visible). - const char* expected_names[] = { kPageAction, kBrowserAction, kSynthetic }; - EXPECT_TRUE(VerifyToolbarOrder(expected_names, 3u, 2u)); - } - - { - // This should not have any effect on the second tab, which should still - // have the original order and visible count. - ActivateTab(1); - const char* expected_names[] = { kBrowserAction, kSynthetic, kPageAction }; - EXPECT_TRUE(VerifyToolbarOrder(expected_names, 3u, 1u)); - } - - { - // Switching back to the first tab should mean that actions that want to run - // are re-popped out. - ActivateTab(0); - const char* expected_names[] = { kPageAction, kBrowserAction, kSynthetic }; - EXPECT_TRUE(VerifyToolbarOrder(expected_names, 3u, 2u)); - } - - { - // Now, set the action to be hidden again, and notify of the change. - SetActionWantsToRunOnTab(action, web_contents, false); - // The order and visible count should return to normal (the page action - // should move back to its original index in overflow). - const char* expected_names[] = { kBrowserAction, kSynthetic, kPageAction }; - EXPECT_TRUE(VerifyToolbarOrder(expected_names, 3u, 1u)); - } - - { - // Move page action to the second index and increase visible size to 2 (so - // it's naturally visible). - toolbar_model()->MoveExtensionIcon(page_action->id(), 1u); - toolbar_model()->SetVisibleIconCount(2u); - const char* expected_names[] = { kBrowserAction, kPageAction, kSynthetic }; - EXPECT_TRUE(VerifyToolbarOrder(expected_names, 3u, 2u)); - // Make the now-visible page action want to act. - // Since the action is already visible, this should have no effect - the - // order and visible count should remain unchanged. - SetActionWantsToRunOnTab(action, web_contents, true); - EXPECT_TRUE(VerifyToolbarOrder(expected_names, 3u, 2u)); - } - - { - // We should still be able to increase the size of the model, and to move - // the page action. - toolbar_model()->SetVisibleIconCount(3); - toolbar_model()->MoveExtensionIcon(page_action->id(), 0u); - const char* expected_names[] = { kPageAction, kBrowserAction, kSynthetic }; - EXPECT_TRUE(VerifyToolbarOrder(expected_names, 3u, 3u)); - // If we moved the page action, the move should remain in effect even after - // the action no longer wants to act. - SetActionWantsToRunOnTab(action, web_contents, false); - EXPECT_TRUE(VerifyToolbarOrder(expected_names, 3u, 3u)); - } - - { - // Test the edge case of having no icons visible. - toolbar_model()->SetVisibleIconCount(0); - const char* expected_names[] = { kPageAction, kBrowserAction, kSynthetic }; - EXPECT_TRUE(VerifyToolbarOrder(expected_names, 3u, 0u)); - SetActionWantsToRunOnTab(action, web_contents, true); - EXPECT_TRUE(VerifyToolbarOrder(expected_names, 3u, 1u)); - } - - // Reset the action, and create another page action extension. - SetActionWantsToRunOnTab(action, web_contents, false); - const char kPageAction2[] = "page action2"; - scoped_refptr<const extensions::Extension> page_action2 = - CreateAndAddExtension( - kPageAction2, extensions::extension_action_test_util::PAGE_ACTION); - // The new extension was installed visible. Move it to the last index, and - // adjust the visible count. - toolbar_model()->MoveExtensionIcon(page_action2->id(), 3u); - toolbar_model()->SetVisibleIconCount(0); - - { - // The second page action should be added to the end, and no icons should - // be visible. - const char* expected_names[] = - { kPageAction, kBrowserAction, kSynthetic, kPageAction2 }; - EXPECT_TRUE(VerifyToolbarOrder(expected_names, 4u, 0u)); - } - - { - // Make both page actions want to run, with the second triggering first. - SetActionWantsToRunOnTab( - action_manager->GetExtensionAction(*page_action2), web_contents, true); - SetActionWantsToRunOnTab(action, web_contents, true); - - // Even though the second page action triggered first, the order of actions - // wanting to run should respect the normal order of actions. - const char* expected_names[] = - { kPageAction, kPageAction2, kBrowserAction, kSynthetic }; - EXPECT_TRUE(VerifyToolbarOrder(expected_names, 4u, 2u)); - } -} - -TEST_F(ToolbarActionsBarPopOutUnitTest, AdjustingActionsThatWantToAct) { - // Add three extensions to the profile; this is the easiest way to have - // toolbar actions. - const char kBrowserAction[] = "browser action"; - const char kPageAction[] = "page action"; - const char kSynthetic[] = "synthetic"; // This has a generated action icon. - - CreateAndAddExtension(kBrowserAction, - extensions::extension_action_test_util::BROWSER_ACTION); - scoped_refptr<const extensions::Extension> page_action = - CreateAndAddExtension( - kPageAction, extensions::extension_action_test_util::PAGE_ACTION); - CreateAndAddExtension(kSynthetic, - extensions::extension_action_test_util::NO_ACTION); - - // Move the page action to the second index and reduce the visible count to 1 - // so that the page action is hidden (and can pop out when it needs to act). - toolbar_model()->SetVisibleIconCount(1); - toolbar_model()->MoveExtensionIcon(page_action->id(), 1u); - - // Create a tab. - AddTab(browser(), GURL("http://www.google.com/")); - AddTab(browser(), GURL("http://www.youtube.com/")); - content::WebContents* web_contents = - browser()->tab_strip_model()->GetWebContentsAt(0); - - extensions::ExtensionActionManager* action_manager = - extensions::ExtensionActionManager::Get(profile()); - ExtensionAction* action = action_manager->GetExtensionAction(*page_action); - ASSERT_TRUE(action); - - { - // Make the page action pop out, which causes the visible count for tab to - // become 2, with the page action at the front. - SetActionWantsToRunOnTab(action, web_contents, true); - const char* expected_names[] = { kPageAction, kBrowserAction, kSynthetic }; - EXPECT_TRUE(VerifyToolbarOrder(expected_names, 3u, 2u)); - } - - { - // Shrink the toolbar count. To the user, this is hiding "browser action", - // so that's the effect it should have (browser action should be hidden from - // all windows). - toolbar_actions_bar()->OnResizeComplete( - toolbar_actions_bar()->IconCountToWidth(1u)); - const char* expected_names[] = { kPageAction, kBrowserAction, kSynthetic }; - EXPECT_TRUE(VerifyToolbarOrder(expected_names, 3u, 1u)); - // Once the action no longer wants to run, "page action" should go back to - // its normal spot, and visible count goes to zero. - SetActionWantsToRunOnTab(action, web_contents, false); - const char* expected_names2[] = { kBrowserAction, kPageAction, kSynthetic }; - EXPECT_TRUE(VerifyToolbarOrder(expected_names2, 3u, 0u)); - } - - { - // Make the action want to run again - it should pop out, and be the only - // action visible on the tab. - SetActionWantsToRunOnTab(action, web_contents, true); - const char* expected_names[] = { kPageAction, kBrowserAction, kSynthetic }; - EXPECT_TRUE(VerifyToolbarOrder(expected_names, 3u, 1u)); - // Set the visible icon count for that tab to 2. This uncovers one action, - // so the base visible count should be 1, and the order should still be - // "page action", "browser action", "synthetic". - toolbar_actions_bar()->OnResizeComplete( - toolbar_actions_bar()->IconCountToWidth(2u)); - EXPECT_TRUE(VerifyToolbarOrder(expected_names, 3u, 2u)); - // Next, grow the item order to 3 for the tab. This uncovers the "synthetic" - // extension. This is interesting, because for the same action to be - // uncovered on other tabs, the underlying order (which was previously - // "browser action", "page action", "synthetic") has to chage (to be - // "browser action", "synthetic", "page action"). If we don't make this - // change, we uncover "synthetic" here, but in other windows, "page action" - // is uncovered (which is weird). Ensure that the change that was visible - // to the user (uncovering "synthetic") is the one that happens to the - // underlying model. - toolbar_actions_bar()->OnResizeComplete( - toolbar_actions_bar()->IconCountToWidth(3u)); - EXPECT_TRUE(VerifyToolbarOrder(expected_names, 3u, 3u)); - // So when "page action" finishes, it should go back to overflow, leaving - // the other two visible. - SetActionWantsToRunOnTab(action, web_contents, false); - const char* expected_names2[] = { kBrowserAction, kSynthetic, kPageAction }; - EXPECT_TRUE(VerifyToolbarOrder(expected_names2, 3u, 2u)); - } - - { - // Next, test that moving an action that was popped out for overflow pins - // the action to the new spot. Since the action originated in overflow, this - // also causes the base visible count to increase. - // Set the action to be visible. - SetActionWantsToRunOnTab(action, web_contents, true); - const char* expected_names[] = { kPageAction, kBrowserAction, kSynthetic }; - EXPECT_TRUE(VerifyToolbarOrder(expected_names, 3u, 3u)); - // Move the popped out "page action" extension to the first index. - toolbar_actions_bar()->OnDragDrop(0, 1, ToolbarActionsBar::DRAG_TO_SAME); - const char* expected_names2[] = { kBrowserAction, kPageAction, kSynthetic }; - EXPECT_TRUE(VerifyToolbarOrder(expected_names2, 3u, 3u)); - // Since this pinned "page action", the order stays the same after the run. - SetActionWantsToRunOnTab(action, web_contents, false); - EXPECT_TRUE(VerifyToolbarOrder(expected_names2, 3u, 3u)); - } - - { - // Move "page action" back to overflow. - toolbar_actions_bar()->OnDragDrop(1, 2, ToolbarActionsBar::DRAG_TO_SAME); - toolbar_actions_bar()->OnResizeComplete( - toolbar_actions_bar()->IconCountToWidth(2u)); - - // Test moving a popped out extension to the overflow menu; this should have - // no effect on the base visible count. - SetActionWantsToRunOnTab(action, web_contents, true); - const char* expected_names[] = { kPageAction, kBrowserAction, kSynthetic }; - EXPECT_TRUE(VerifyToolbarOrder(expected_names, 3u, 3u)); - // Move "page action" to the overflow menu. - toolbar_actions_bar()->OnDragDrop( - 0, 2, ToolbarActionsBar::DRAG_TO_OVERFLOW); - const char* expected_names2[] = { kBrowserAction, kSynthetic, kPageAction }; - EXPECT_TRUE(VerifyToolbarOrder(expected_names2, 3u, 2u)); - SetActionWantsToRunOnTab(action, web_contents, false); - EXPECT_TRUE(VerifyToolbarOrder(expected_names2, 3u, 2u)); - } -}
diff --git a/chrome/browser/ui/toolbar/wrench_menu_model.cc b/chrome/browser/ui/toolbar/wrench_menu_model.cc index e6a923d..9f27c2e4 100644 --- a/chrome/browser/ui/toolbar/wrench_menu_model.cc +++ b/chrome/browser/ui/toolbar/wrench_menu_model.cc
@@ -96,6 +96,42 @@ } } +#if defined(OS_WIN) +bool GetRestartMenuItemIfRequired(const chrome::HostDesktopType& desktop_type, + int* command_id, + int* string_id) { + if (base::win::GetVersion() == base::win::VERSION_WIN8 || + base::win::GetVersion() == base::win::VERSION_WIN8_1) { + if (desktop_type != chrome::HOST_DESKTOP_TYPE_ASH) { + *command_id = IDC_WIN8_METRO_RESTART; + *string_id = IDS_WIN8_METRO_RESTART; + } else { + *command_id = IDC_WIN_DESKTOP_RESTART; + *string_id = IDS_WIN_DESKTOP_RESTART; + } + return true; + } + + // Windows 7 ASH mode is only supported in DEBUG for now. +#if !defined(NDEBUG) + // Windows 8 can support ASH mode using WARP, but Windows 7 requires a working + // GPU compositor. + if (base::win::GetVersion() == base::win::VERSION_WIN7 && + content::GpuDataManager::GetInstance()->CanUseGpuBrowserCompositor()) { + if (desktop_type != chrome::HOST_DESKTOP_TYPE_ASH) { + *command_id = IDC_WIN_CHROMEOS_RESTART; + *string_id = IDS_WIN_CHROMEOS_RESTART; + } else { + *command_id = IDC_WIN_DESKTOP_RESTART; + *string_id = IDS_WIN_DESKTOP_RESTART; + } + return true; + } +#endif + return false; +} +#endif + } // namespace //////////////////////////////////////////////////////////////////////////////// @@ -926,30 +962,13 @@ #endif #if defined(OS_WIN) - base::win::Version min_version_for_ash_mode = base::win::VERSION_WIN8; - // Windows 7 ASH mode is only supported in DEBUG for now. -#if !defined(NDEBUG) - min_version_for_ash_mode = base::win::VERSION_WIN7; -#endif - // Windows 8 can support ASH mode using WARP, but Windows 7 requires a working - // GPU compositor. - if ((base::win::GetVersion() >= min_version_for_ash_mode && - content::GpuDataManager::GetInstance()->CanUseGpuBrowserCompositor()) || - (base::win::GetVersion() >= base::win::VERSION_WIN8)) { - if (browser_->host_desktop_type() == chrome::HOST_DESKTOP_TYPE_ASH) { - // ASH/Metro mode, add the 'Relaunch Chrome in desktop mode'. - AddSeparator(ui::NORMAL_SEPARATOR); - AddItemWithStringId(IDC_WIN_DESKTOP_RESTART, IDS_WIN_DESKTOP_RESTART); - } else { - // In Windows 8 desktop, add the 'Relaunch Chrome in Windows 8 mode'. - // In Windows 7 desktop, add the 'Relaunch Chrome in Windows ASH mode' - AddSeparator(ui::NORMAL_SEPARATOR); - if (base::win::GetVersion() == base::win::VERSION_WIN8 || - base::win::GetVersion() == base::win::VERSION_WIN8_1) - AddItemWithStringId(IDC_WIN8_METRO_RESTART, IDS_WIN8_METRO_RESTART); - else - AddItemWithStringId(IDC_WIN_CHROMEOS_RESTART, IDS_WIN_CHROMEOS_RESTART); - } + int command_id = IDC_WIN_DESKTOP_RESTART; + int string_id = IDS_WIN_DESKTOP_RESTART; + if (GetRestartMenuItemIfRequired(browser_->host_desktop_type(), + &command_id, + &string_id)) { + AddSeparator(ui::NORMAL_SEPARATOR); + AddItemWithStringId(command_id, string_id); } #endif
diff --git a/chrome/browser/ui/views/apps/app_window_easy_resize_window_targeter.cc b/chrome/browser/ui/views/apps/app_window_easy_resize_window_targeter.cc new file mode 100644 index 0000000..98be8215 --- /dev/null +++ b/chrome/browser/ui/views/apps/app_window_easy_resize_window_targeter.cc
@@ -0,0 +1,33 @@ +// 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 "chrome/browser/ui/views/apps/app_window_easy_resize_window_targeter.h" + +#include "ui/aura/window.h" +#include "ui/base/base_window.h" + +AppWindowEasyResizeWindowTargeter::AppWindowEasyResizeWindowTargeter( + aura::Window* aura_window, + const gfx::Insets& insets, + ui::BaseWindow* native_app_window) + : wm::EasyResizeWindowTargeter(aura_window, insets, insets), + native_app_window_(native_app_window) { +} + +AppWindowEasyResizeWindowTargeter::~AppWindowEasyResizeWindowTargeter() { +} + +bool AppWindowEasyResizeWindowTargeter::EventLocationInsideBounds( + ui::EventTarget* target, + const ui::LocatedEvent& event) const { + aura::Window* window = static_cast<aura::Window*>(target); + // EasyResizeWindowTargeter intercepts events landing at the edges of the + // window. Since maximized and fullscreen windows can't be resized anyway, + // skip EasyResizeWindowTargeter so that the web contents receive all mouse + // events. + if (native_app_window_->IsMaximized() || native_app_window_->IsFullscreen()) + return WindowTargeter::EventLocationInsideBounds(window, event); + else + return EasyResizeWindowTargeter::EventLocationInsideBounds(window, event); +}
diff --git a/chrome/browser/ui/views/apps/app_window_easy_resize_window_targeter.h b/chrome/browser/ui/views/apps/app_window_easy_resize_window_targeter.h new file mode 100644 index 0000000..69b08d6 --- /dev/null +++ b/chrome/browser/ui/views/apps/app_window_easy_resize_window_targeter.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 CHROME_BROWSER_UI_VIEWS_APPS_APP_WINDOW_EASY_RESIZE_WINDOW_TARGETER_H_ +#define CHROME_BROWSER_UI_VIEWS_APPS_APP_WINDOW_EASY_RESIZE_WINDOW_TARGETER_H_ + +#include "ui/wm/core/easy_resize_window_targeter.h" + +namespace ui { +class BaseWindow; +} + +// An EasyResizeEventTargeter whose behavior depends on the state of the app +// window. +class AppWindowEasyResizeWindowTargeter : public wm::EasyResizeWindowTargeter { + public: + // |aura_window| is the owner of this targeter. + AppWindowEasyResizeWindowTargeter(aura::Window* aura_window, + const gfx::Insets& insets, + ui::BaseWindow* native_app_window); + + ~AppWindowEasyResizeWindowTargeter() override; + + protected: + // ui::EventTargeter: + bool EventLocationInsideBounds(ui::EventTarget* target, + const ui::LocatedEvent& event) const override; + + private: + ui::BaseWindow* native_app_window_; + + DISALLOW_COPY_AND_ASSIGN(AppWindowEasyResizeWindowTargeter); +}; + +#endif // CHROME_BROWSER_UI_VIEWS_APPS_APP_WINDOW_EASY_RESIZE_WINDOW_TARGETER_H_
diff --git a/chrome/browser/ui/views/apps/chrome_native_app_window_views_aura.cc b/chrome/browser/ui/views/apps/chrome_native_app_window_views_aura.cc index 8535c131..6fcaf86 100644 --- a/chrome/browser/ui/views/apps/chrome_native_app_window_views_aura.cc +++ b/chrome/browser/ui/views/apps/chrome_native_app_window_views_aura.cc
@@ -18,6 +18,7 @@ #include "chrome/browser/ui/ash/ash_util.h" #include "chrome/browser/ui/ash/multi_user/multi_user_context_menu.h" #include "chrome/browser/ui/host_desktop.h" +#include "chrome/browser/ui/views/apps/app_window_easy_resize_window_targeter.h" #include "chrome/browser/ui/views/apps/shaped_app_window_targeter.h" #include "chrome/browser/web_applications/web_app.h" #include "ui/aura/client/aura_constants.h" @@ -28,7 +29,6 @@ #include "ui/gfx/image/image_skia.h" #include "ui/views/controls/menu/menu_runner.h" #include "ui/views/widget/widget.h" -#include "ui/wm/core/easy_resize_window_targeter.h" #if defined(OS_CHROMEOS) #include "ash/shell_window_ids.h" @@ -180,11 +180,11 @@ int resize_inside = frame->resize_inside_bounds_size(); gfx::Insets inset(resize_inside, resize_inside, resize_inside, resize_inside); - // Add the EasyResizeWindowTargeter on the window, not its root window. The - // root window does not have a delegate, which is needed to handle the event - // in Linux. + // Add the AppWindowEasyResizeWindowTargeter on the window, not its root + // window. The root window does not have a delegate, which is needed to + // handle the event in Linux. window->SetEventTargeter(scoped_ptr<ui::EventTargeter>( - new wm::EasyResizeWindowTargeter(window, inset, inset))); + new AppWindowEasyResizeWindowTargeter(window, inset, this))); } #endif
diff --git a/chrome/browser/ui/views/autofill/card_unmask_prompt_views.cc b/chrome/browser/ui/views/autofill/card_unmask_prompt_views.cc index e49e0b8..984aee66 100644 --- a/chrome/browser/ui/views/autofill/card_unmask_prompt_views.cc +++ b/chrome/browser/ui/views/autofill/card_unmask_prompt_views.cc
@@ -328,7 +328,8 @@ const gfx::Animation* animation) { uint8_t alpha = static_cast<uint8_t>(animation->CurrentValueBetween(0, 255)); progress_overlay_->SetAlpha(alpha); - storage_row_->SetAlpha(255 - alpha); + if (storage_row_) + storage_row_->SetAlpha(255 - alpha); } void CardUnmaskPromptViews::InitIfNecessary() { @@ -433,7 +434,7 @@ progress_overlay_->SetVisible(false); AddChildView(progress_overlay_); - progress_throbber_ = new views::CheckmarkThrobber(); + progress_throbber_ = new views::MaterialThrobber(); progress_overlay_->AddChildView(progress_throbber_); progress_label_ = new views::Label(l10n_util::GetStringUTF16(
diff --git a/chrome/browser/ui/views/autofill/card_unmask_prompt_views.h b/chrome/browser/ui/views/autofill/card_unmask_prompt_views.h index 6318284..b272480 100644 --- a/chrome/browser/ui/views/autofill/card_unmask_prompt_views.h +++ b/chrome/browser/ui/views/autofill/card_unmask_prompt_views.h
@@ -17,7 +17,7 @@ class ImageView; class Label; class Checkbox; -class CheckmarkThrobber; +class MaterialThrobber; } namespace autofill { @@ -135,7 +135,7 @@ views::Checkbox* storage_checkbox_; FadeOutView* progress_overlay_; - views::CheckmarkThrobber* progress_throbber_; + views::MaterialThrobber* progress_throbber_; views::Label* progress_label_; gfx::SlideAnimation overlay_animation_;
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_bubble_view.cc b/chrome/browser/ui/views/bookmarks/bookmark_bubble_view.cc index 98a9bc6..9808dc1 100644 --- a/chrome/browser/ui/views/bookmarks/bookmark_bubble_view.cc +++ b/chrome/browser/ui/views/bookmarks/bookmark_bubble_view.cc
@@ -68,7 +68,7 @@ Profile* profile, const GURL& url, bool newly_bookmarked) { - if (IsShowing()) + if (bookmark_bubble_) return; bookmark_bubble_ = new BookmarkBubbleView(anchor_view, @@ -86,13 +86,8 @@ bookmark_bubble_->observer_->OnBookmarkBubbleShown(url); } -// static -bool BookmarkBubbleView::IsShowing() { - return bookmark_bubble_ != NULL; -} - void BookmarkBubbleView::Hide() { - if (IsShowing()) + if (bookmark_bubble_) bookmark_bubble_->GetWidget()->Close(); }
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_bubble_view.h b/chrome/browser/ui/views/bookmarks/bookmark_bubble_view.h index 08e46d9..51892ab 100644 --- a/chrome/browser/ui/views/bookmarks/bookmark_bubble_view.h +++ b/chrome/browser/ui/views/bookmarks/bookmark_bubble_view.h
@@ -40,10 +40,10 @@ const GURL& url, bool newly_bookmarked); - static bool IsShowing(); - static void Hide(); + static BookmarkBubbleView* bookmark_bubble() { return bookmark_bubble_; } + ~BookmarkBubbleView() override; // views::WidgetDelegate:
diff --git a/chrome/browser/ui/views/chrome_constrained_window_views_client.cc b/chrome/browser/ui/views/chrome_constrained_window_views_client.cc index 767d96d9..2143f22c 100644 --- a/chrome/browser/ui/views/chrome_constrained_window_views_client.cc +++ b/chrome/browser/ui/views/chrome_constrained_window_views_client.cc
@@ -21,17 +21,8 @@ // ConstrainedWindowViewsClient: content::WebContents* GetEmbedderWebContents( content::WebContents* initiator_web_contents) override { - // |initiator_web_contents| may be embedded within a chain of nested - // GuestViews. If it is, follow the chain of embedders to the outermost - // WebContents and return it. - while (extensions::GuestViewBase* guest_view = - extensions::GuestViewBase::FromWebContents( - initiator_web_contents)) { - if (!guest_view->embedder_web_contents()) - break; - initiator_web_contents = guest_view->embedder_web_contents(); - } - return initiator_web_contents; + return extensions::GuestViewBase::GetTopLevelWebContents( + initiator_web_contents); } web_modal::ModalDialogHost* GetModalDialogHost( gfx::NativeWindow parent) override {
diff --git a/chrome/browser/ui/views/extensions/bookmark_override_browsertest.cc b/chrome/browser/ui/views/extensions/bookmark_override_browsertest.cc index c61b1e5..aa3f4ae 100644 --- a/chrome/browser/ui/views/extensions/bookmark_override_browsertest.cc +++ b/chrome/browser/ui/views/extensions/bookmark_override_browsertest.cc
@@ -58,11 +58,11 @@ ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON); // Verify that clicking once shows the bookmark bubble. - EXPECT_FALSE(BookmarkBubbleView::IsShowing()); + EXPECT_FALSE(BookmarkBubbleView::bookmark_bubble()); star_view->OnMousePressed(pressed_event); - EXPECT_FALSE(BookmarkBubbleView::IsShowing()); + EXPECT_FALSE(BookmarkBubbleView::bookmark_bubble()); star_view->OnMouseReleased(released_event); - EXPECT_TRUE(BookmarkBubbleView::IsShowing()); + EXPECT_TRUE(BookmarkBubbleView::bookmark_bubble()); } // Test that invoking the IDC_BOOKMARK_PAGE command (as done by the wrench menu) @@ -84,7 +84,7 @@ // Check that the BookmarkBubbleView is shown when executing // IDC_BOOKMARK_PAGE. - EXPECT_FALSE(BookmarkBubbleView::IsShowing()); + EXPECT_FALSE(BookmarkBubbleView::bookmark_bubble()); chrome::ExecuteCommand(browser(), IDC_BOOKMARK_PAGE); - EXPECT_TRUE(BookmarkBubbleView::IsShowing()); + EXPECT_TRUE(BookmarkBubbleView::bookmark_bubble()); }
diff --git a/chrome/browser/ui/views/extensions/extension_message_bubble_view_browsertest.cc b/chrome/browser/ui/views/extensions/extension_message_bubble_view_browsertest.cc index 771f41d2..99de121 100644 --- a/chrome/browser/ui/views/extensions/extension_message_bubble_view_browsertest.cc +++ b/chrome/browser/ui/views/extensions/extension_message_bubble_view_browsertest.cc
@@ -2,20 +2,20 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "base/run_loop.h" -#include "chrome/browser/extensions/extension_action_test_util.h" -#include "chrome/browser/extensions/extension_service.h" -#include "chrome/browser/ui/extensions/extension_message_bubble_factory.h" -#include "chrome/browser/ui/toolbar/browser_actions_bar_browsertest.h" +#include "chrome/browser/ui/extensions/extension_message_bubble_browsertest.h" #include "chrome/browser/ui/views/extensions/extension_message_bubble_view.h" #include "chrome/browser/ui/views/frame/browser_view.h" #include "chrome/browser/ui/views/toolbar/browser_actions_container.h" #include "chrome/browser/ui/views/toolbar/toolbar_view.h" #include "chrome/browser/ui/views/toolbar/wrench_toolbar_button.h" -#include "extensions/common/feature_switch.h" namespace { +// Returns the ToolbarView for the given |browser|. +ToolbarView* GetToolbarViewForBrowser(Browser* browser) { + return BrowserView::GetBrowserViewForBrowser(browser)->toolbar(); +} + // Checks that the |bubble| is using the |expected_reference_view|, and is in // approximately the correct position. void CheckBubbleAndReferenceView(views::BubbleDelegateView* bubble, @@ -47,100 +47,61 @@ } // namespace class ExtensionMessageBubbleViewBrowserTest - : public BrowserActionsBarBrowserTest { + : public ExtensionMessageBubbleBrowserTest { protected: ExtensionMessageBubbleViewBrowserTest() {} ~ExtensionMessageBubbleViewBrowserTest() override {} - void SetUpCommandLine(base::CommandLine* command_line) override; - private: - scoped_ptr<extensions::FeatureSwitch::ScopedOverride> - dev_mode_bubble_override_; + // ExtensionMessageBubbleBrowserTest: + void CheckBubble(Browser* browser, AnchorPosition anchor) override; + void CloseBubble(Browser* browser) override; DISALLOW_COPY_AND_ASSIGN(ExtensionMessageBubbleViewBrowserTest); }; -void ExtensionMessageBubbleViewBrowserTest::SetUpCommandLine( - base::CommandLine* command_line) { - BrowserActionsBarBrowserTest::SetUpCommandLine(command_line); - // The dev mode warning bubble is an easy one to trigger, so we use that for - // our testing purposes. - dev_mode_bubble_override_.reset( - new extensions::FeatureSwitch::ScopedOverride( - extensions::FeatureSwitch::force_dev_mode_highlighting(), - true)); - ExtensionMessageBubbleFactory::set_enabled_for_tests(true); +void ExtensionMessageBubbleViewBrowserTest::CheckBubble(Browser* browser, + AnchorPosition anchor) { + ToolbarView* toolbar_view = GetToolbarViewForBrowser(browser); + BrowserActionsContainer* container = toolbar_view->browser_actions(); + views::BubbleDelegateView* bubble = container->active_bubble(); + views::View* anchor_view = nullptr; + switch (anchor) { + case ANCHOR_BROWSER_ACTION: + anchor_view = container->GetToolbarActionViewAt(0); + break; + case ANCHOR_WRENCH_MENU: + anchor_view = toolbar_view->app_menu(); + break; + } + CheckBubbleAndReferenceView(bubble, anchor_view); } -// Tests that an extension bubble will be anchored to the wrench menu when there -// aren't any extensions with actions. -// This also tests that the crashes in crbug.com/476426 are fixed. -IN_PROC_BROWSER_TEST_F(ExtensionMessageBubbleViewBrowserTest, - ExtensionBubbleAnchoredToWrenchMenu) { - scoped_refptr<const extensions::Extension> action_extension = - extensions::extension_action_test_util::CreateActionExtension( - "action_extension", - extensions::extension_action_test_util::BROWSER_ACTION, - extensions::Manifest::UNPACKED); - extension_service()->AddExtension(action_extension.get()); - - Browser* second_browser = new Browser( - Browser::CreateParams(profile(), browser()->host_desktop_type())); - base::RunLoop().RunUntilIdle(); - - BrowserActionsContainer* second_container = - BrowserView::GetBrowserViewForBrowser(second_browser)->toolbar()-> - browser_actions(); - views::BubbleDelegateView* bubble = second_container->active_bubble(); - CheckBubbleAndReferenceView(bubble, - second_container->GetToolbarActionViewAt(0)); - +void ExtensionMessageBubbleViewBrowserTest::CloseBubble(Browser* browser) { + BrowserActionsContainer* container = + GetToolbarViewForBrowser(browser)->browser_actions(); + views::BubbleDelegateView* bubble = container->active_bubble(); + ASSERT_TRUE(bubble); bubble->GetWidget()->Close(); - EXPECT_EQ(nullptr, second_container->active_bubble()); + EXPECT_EQ(nullptr, container->active_bubble()); } -// Tests that an extension bubble will be anchored to an extension action when -// there are extensions with actions. IN_PROC_BROWSER_TEST_F(ExtensionMessageBubbleViewBrowserTest, ExtensionBubbleAnchoredToExtensionAction) { - scoped_refptr<const extensions::Extension> no_action_extension = - extensions::extension_action_test_util::CreateActionExtension( - "action_extension", - extensions::extension_action_test_util::NO_ACTION, - extensions::Manifest::UNPACKED); - extension_service()->AddExtension(no_action_extension.get()); - - Browser* second_browser = new Browser( - Browser::CreateParams(profile(), browser()->host_desktop_type())); - ASSERT_TRUE(second_browser); - base::RunLoop().RunUntilIdle(); - - ToolbarView* toolbar = - BrowserView::GetBrowserViewForBrowser(second_browser)->toolbar(); - BrowserActionsContainer* second_container = toolbar->browser_actions(); - views::BubbleDelegateView* bubble = second_container->active_bubble(); - CheckBubbleAndReferenceView(bubble, toolbar->app_menu()); - - bubble->GetWidget()->Close(); - EXPECT_EQ(nullptr, second_container->active_bubble()); + TestBubbleAnchoredToExtensionAction(); } -// Tests that the extension bubble will show on startup. +IN_PROC_BROWSER_TEST_F(ExtensionMessageBubbleViewBrowserTest, + ExtensionBubbleAnchoredToWrenchMenu) { + TestBubbleAnchoredToWrenchMenu(); +} + IN_PROC_BROWSER_TEST_F(ExtensionMessageBubbleViewBrowserTest, PRE_ExtensionBubbleShowsOnStartup) { - LoadExtension(test_data_dir_.AppendASCII("good_unpacked")); + PreBubbleShowsOnStartup(); } IN_PROC_BROWSER_TEST_F(ExtensionMessageBubbleViewBrowserTest, ExtensionBubbleShowsOnStartup) { - base::RunLoop().RunUntilIdle(); - BrowserActionsContainer* container = - BrowserView::GetBrowserViewForBrowser(browser())->toolbar()-> - browser_actions(); - views::BubbleDelegateView* bubble = container->active_bubble(); - CheckBubbleAndReferenceView(bubble, container->GetToolbarActionViewAt(0)); - - bubble->GetWidget()->Close(); - EXPECT_EQ(nullptr, container->active_bubble()); + TestBubbleShowsOnStartup(); }
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 5868fa9..c5a6fb1 100644 --- a/chrome/browser/ui/views/frame/glass_browser_frame_view.cc +++ b/chrome/browser/ui/views/frame/glass_browser_frame_view.cc
@@ -581,12 +581,21 @@ // This will reset the icon which we set in the throbber code. // WM_SETICON with null icon restores the icon for title bar but not // for taskbar. See http://crbug.com/29996 - SendMessage(views::HWNDForWidget(frame()), WM_SETICON, - static_cast<WPARAM>(ICON_SMALL), - reinterpret_cast<LPARAM>(small_icon)); - SendMessage(views::HWNDForWidget(frame()), WM_SETICON, - static_cast<WPARAM>(ICON_BIG), - reinterpret_cast<LPARAM>(big_icon)); + HICON previous_small_icon = reinterpret_cast<HICON>( + SendMessage(views::HWNDForWidget(frame()), WM_SETICON, + static_cast<WPARAM>(ICON_SMALL), + reinterpret_cast<LPARAM>(small_icon))); + + HICON previous_big_icon = reinterpret_cast<HICON>( + SendMessage(views::HWNDForWidget(frame()), WM_SETICON, + static_cast<WPARAM>(ICON_BIG), + reinterpret_cast<LPARAM>(big_icon))); + + if (previous_small_icon) + ::DestroyIcon(previous_small_icon); + + if (previous_big_icon) + ::DestroyIcon(previous_big_icon); } }
diff --git a/chrome/browser/ui/views/location_bar/bubble_icon_view.cc b/chrome/browser/ui/views/location_bar/bubble_icon_view.cc index d74f329..8d49ced 100644 --- a/chrome/browser/ui/views/location_bar/bubble_icon_view.cc +++ b/chrome/browser/ui/views/location_bar/bubble_icon_view.cc
@@ -7,6 +7,7 @@ #include "chrome/browser/command_updater.h" #include "ui/accessibility/ax_view_state.h" #include "ui/events/event.h" +#include "ui/views/bubble/bubble_delegate.h" BubbleIconView::BubbleIconView(CommandUpdater* command_updater, int command_id) : command_updater_(command_updater), @@ -18,6 +19,12 @@ BubbleIconView::~BubbleIconView() { } +bool BubbleIconView::IsBubbleShowing() const { + // If the bubble is being destroyed, it's considered showing though it may be + // already invisible currently. + return GetBubble() != NULL; +} + void BubbleIconView::GetAccessibleState(ui::AXViewState* state) { views::ImageView::GetAccessibleState(state); state->role = ui::AX_ROLE_BUTTON; @@ -74,3 +81,9 @@ if (command_updater_) command_updater_->ExecuteCommand(command_id_); } + +void BubbleIconView::OnBoundsChanged(const gfx::Rect& previous_bounds) { + views::BubbleDelegateView* bubble = GetBubble(); + if (bubble) + bubble->OnAnchorBoundsChanged(); +}
diff --git a/chrome/browser/ui/views/location_bar/bubble_icon_view.h b/chrome/browser/ui/views/location_bar/bubble_icon_view.h index 504cf870..94e6143 100644 --- a/chrome/browser/ui/views/location_bar/bubble_icon_view.h +++ b/chrome/browser/ui/views/location_bar/bubble_icon_view.h
@@ -9,6 +9,10 @@ class CommandUpdater; +namespace views { +class BubbleDelegateView; +} + // Represents an icon on the omnibox that shows a bubble when clicked. class BubbleIconView : public views::ImageView { protected: @@ -22,7 +26,7 @@ ~BubbleIconView() override; // Returns true if a related bubble is showing. - virtual bool IsBubbleShowing() const = 0; + bool IsBubbleShowing() const; // Invoked prior to executing the command. virtual void OnExecuting(ExecuteSource execute_source) = 0; @@ -42,6 +46,12 @@ // Calls OnExecuting and runs |command_id_| with a valid |command_updater_|. virtual void ExecuteCommand(ExecuteSource source); + // Returns the bubble instance for the icon. + virtual views::BubbleDelegateView* GetBubble() const = 0; + + // views::View: + void OnBoundsChanged(const gfx::Rect& previous_bounds) override; + private: // The CommandUpdater for the Browser object that owns the location bar. CommandUpdater* command_updater_;
diff --git a/chrome/browser/ui/views/location_bar/star_view.cc b/chrome/browser/ui/views/location_bar/star_view.cc index 368119d..080d5255 100644 --- a/chrome/browser/ui/views/location_bar/star_view.cc +++ b/chrome/browser/ui/views/location_bar/star_view.cc
@@ -32,10 +32,6 @@ on ? IDR_STAR_LIT : IDR_STAR)); } -bool StarView::IsBubbleShowing() const { - return BookmarkBubbleView::IsShowing(); -} - void StarView::OnExecuting( BubbleIconView::ExecuteSource execute_source) { BookmarkEntryPoint entry_point = BOOKMARK_ENTRY_POINT_STAR_MOUSE; @@ -63,3 +59,7 @@ BubbleIconView::ExecuteCommand(source); } } + +views::BubbleDelegateView* StarView::GetBubble() const { + return BookmarkBubbleView::bookmark_bubble(); +}
diff --git a/chrome/browser/ui/views/location_bar/star_view.h b/chrome/browser/ui/views/location_bar/star_view.h index 1b0e63c..57da0f2 100644 --- a/chrome/browser/ui/views/location_bar/star_view.h +++ b/chrome/browser/ui/views/location_bar/star_view.h
@@ -21,9 +21,9 @@ protected: // BubbleIconView: - bool IsBubbleShowing() const override; void OnExecuting(BubbleIconView::ExecuteSource execute_source) override; void ExecuteCommand(ExecuteSource source) override; + views::BubbleDelegateView* GetBubble() const override; private: Browser* browser_;
diff --git a/chrome/browser/ui/views/location_bar/star_view_browsertest.cc b/chrome/browser/ui/views/location_bar/star_view_browsertest.cc index 717562ba..f5c620df 100644 --- a/chrome/browser/ui/views/location_bar/star_view_browsertest.cc +++ b/chrome/browser/ui/views/location_bar/star_view_browsertest.cc
@@ -50,11 +50,11 @@ ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON); // Verify that clicking once shows the bookmark bubble. - EXPECT_FALSE(BookmarkBubbleView::IsShowing()); + EXPECT_FALSE(BookmarkBubbleView::bookmark_bubble()); star_view->OnMousePressed(pressed_event); - EXPECT_FALSE(BookmarkBubbleView::IsShowing()); + EXPECT_FALSE(BookmarkBubbleView::bookmark_bubble()); star_view->OnMouseReleased(released_event); - EXPECT_TRUE(BookmarkBubbleView::IsShowing()); + EXPECT_TRUE(BookmarkBubbleView::bookmark_bubble()); // Verify that clicking again doesn't reshow it. star_view->OnMousePressed(pressed_event); @@ -62,9 +62,9 @@ // the event processing. BookmarkBubbleView::Hide(); base::MessageLoop::current()->RunUntilIdle(); - EXPECT_FALSE(BookmarkBubbleView::IsShowing()); + EXPECT_FALSE(BookmarkBubbleView::bookmark_bubble()); star_view->OnMouseReleased(released_event); - EXPECT_FALSE(BookmarkBubbleView::IsShowing()); + EXPECT_FALSE(BookmarkBubbleView::bookmark_bubble()); } #if defined(OS_WIN) @@ -138,7 +138,7 @@ runner->QuitClosure()); runner->Run(); - EXPECT_TRUE(BookmarkBubbleView::IsShowing()); + EXPECT_TRUE(BookmarkBubbleView::bookmark_bubble()); result = GetWindowRgnBox(child, ®ion_after); if (result == NULLREGION) {
diff --git a/chrome/browser/ui/views/location_bar/translate_icon_view.cc b/chrome/browser/ui/views/location_bar/translate_icon_view.cc index 3a7add4..a73564f2 100644 --- a/chrome/browser/ui/views/location_bar/translate_icon_view.cc +++ b/chrome/browser/ui/views/location_bar/translate_icon_view.cc
@@ -29,10 +29,10 @@ on ? IDR_TRANSLATE_ACTIVE : IDR_TRANSLATE)); } -bool TranslateIconView::IsBubbleShowing() const { - return TranslateBubbleView::IsShowing(); +void TranslateIconView::OnExecuting( + BubbleIconView::ExecuteSource execute_source) { } -void TranslateIconView::OnExecuting( - BubbleIconView::ExecuteSource execute_source) { +views::BubbleDelegateView* TranslateIconView::GetBubble() const { + return TranslateBubbleView::GetCurrentBubble(); }
diff --git a/chrome/browser/ui/views/location_bar/translate_icon_view.h b/chrome/browser/ui/views/location_bar/translate_icon_view.h index 4ebafd3..bc57187 100644 --- a/chrome/browser/ui/views/location_bar/translate_icon_view.h +++ b/chrome/browser/ui/views/location_bar/translate_icon_view.h
@@ -21,8 +21,8 @@ protected: // BubbleIconView: - bool IsBubbleShowing() const override; void OnExecuting(BubbleIconView::ExecuteSource execute_source) override; + views::BubbleDelegateView* GetBubble() const override; private: DISALLOW_COPY_AND_ASSIGN(TranslateIconView);
diff --git a/chrome/browser/ui/views/location_bar/zoom_bubble_view.cc b/chrome/browser/ui/views/location_bar/zoom_bubble_view.cc index eff898a..9308b6dc 100644 --- a/chrome/browser/ui/views/location_bar/zoom_bubble_view.cc +++ b/chrome/browser/ui/views/location_bar/zoom_bubble_view.cc
@@ -101,14 +101,7 @@ } // static -bool ZoomBubbleView::IsShowing() { - // The bubble is considered showing while closing. - return zoom_bubble_ != NULL && (zoom_bubble_->GetWidget()->IsVisible() || - zoom_bubble_->GetWidget()->IsClosed()); -} - -// static -const ZoomBubbleView* ZoomBubbleView::GetZoomBubbleForTest() { +ZoomBubbleView* ZoomBubbleView::GetZoomBubble() { return zoom_bubble_; }
diff --git a/chrome/browser/ui/views/location_bar/zoom_bubble_view.h b/chrome/browser/ui/views/location_bar/zoom_bubble_view.h index b8039e6..83bb3f34 100644 --- a/chrome/browser/ui/views/location_bar/zoom_bubble_view.h +++ b/chrome/browser/ui/views/location_bar/zoom_bubble_view.h
@@ -43,12 +43,9 @@ // Closes the showing bubble (if one exists). static void CloseBubble(); - // Whether the zoom bubble is currently showing. - static bool IsShowing(); - // Returns the zoom bubble if the zoom bubble is showing. Returns NULL // otherwise. - static const ZoomBubbleView* GetZoomBubbleForTest(); + static ZoomBubbleView* GetZoomBubble(); private: FRIEND_TEST_ALL_PREFIXES(ZoomBubbleBrowserTest, ImmersiveFullscreen);
diff --git a/chrome/browser/ui/views/location_bar/zoom_bubble_view_browsertest.cc b/chrome/browser/ui/views/location_bar/zoom_bubble_view_browsertest.cc index 207c029..db9e61c6 100644 --- a/chrome/browser/ui/views/location_bar/zoom_bubble_view_browsertest.cc +++ b/chrome/browser/ui/views/location_bar/zoom_bubble_view_browsertest.cc
@@ -27,8 +27,8 @@ // The zoom bubble should be anchored when not in fullscreen. ZoomBubbleView::ShowBubble(web_contents, true); - ASSERT_TRUE(ZoomBubbleView::IsShowing()); - const ZoomBubbleView* zoom_bubble = ZoomBubbleView::GetZoomBubbleForTest(); + ASSERT_TRUE(ZoomBubbleView::GetZoomBubble()); + const ZoomBubbleView* zoom_bubble = ZoomBubbleView::GetZoomBubble(); EXPECT_TRUE(zoom_bubble->GetAnchorView()); // Entering fullscreen should close the bubble. (We enter into tab fullscreen @@ -45,13 +45,13 @@ waiter->Wait(); } ASSERT_FALSE(browser_view->immersive_mode_controller()->IsEnabled()); - EXPECT_FALSE(ZoomBubbleView::IsShowing()); + EXPECT_FALSE(ZoomBubbleView::GetZoomBubble()); // The bubble should not be anchored when it is shown in non-immersive // fullscreen. ZoomBubbleView::ShowBubble(web_contents, true); - ASSERT_TRUE(ZoomBubbleView::IsShowing()); - zoom_bubble = ZoomBubbleView::GetZoomBubbleForTest(); + ASSERT_TRUE(ZoomBubbleView::GetZoomBubble()); + zoom_bubble = ZoomBubbleView::GetZoomBubble(); EXPECT_FALSE(zoom_bubble->GetAnchorView()); // Exit fullscreen before ending the test for the sake of sanity. @@ -88,8 +88,8 @@ // The zoom bubble should not be anchored when it is shown in immersive // fullscreen and the top-of-window views are not revealed. ZoomBubbleView::ShowBubble(web_contents, true); - ASSERT_TRUE(ZoomBubbleView::IsShowing()); - const ZoomBubbleView* zoom_bubble = ZoomBubbleView::GetZoomBubbleForTest(); + ASSERT_TRUE(ZoomBubbleView::GetZoomBubble()); + const ZoomBubbleView* zoom_bubble = ZoomBubbleView::GetZoomBubble(); EXPECT_FALSE(zoom_bubble->GetAnchorView()); // An immersive reveal should hide the zoom bubble. @@ -102,8 +102,8 @@ // The zoom bubble should be anchored when it is shown in immersive fullscreen // and the top-of-window views are revealed. ZoomBubbleView::ShowBubble(web_contents, true); - ASSERT_TRUE(ZoomBubbleView::IsShowing()); - zoom_bubble = ZoomBubbleView::GetZoomBubbleForTest(); + ASSERT_TRUE(ZoomBubbleView::GetZoomBubble()); + zoom_bubble = ZoomBubbleView::GetZoomBubble(); EXPECT_TRUE(zoom_bubble->GetAnchorView()); // The top-of-window views should not hide till the zoom bubble hides. (It
diff --git a/chrome/browser/ui/views/location_bar/zoom_view.cc b/chrome/browser/ui/views/location_bar/zoom_view.cc index 6883537..a2f79cd 100644 --- a/chrome/browser/ui/views/location_bar/zoom_view.cc +++ b/chrome/browser/ui/views/location_bar/zoom_view.cc
@@ -47,10 +47,6 @@ SetVisible(true); } -bool ZoomView::IsBubbleShowing() const { - return ZoomBubbleView::IsShowing(); -} - void ZoomView::OnExecuting(BubbleIconView::ExecuteSource source) { ZoomBubbleView::ShowBubble(location_bar_delegate_->GetWebContents(), false); } @@ -59,3 +55,7 @@ BubbleIconView::GetAccessibleState(state); state->name = l10n_util::GetStringUTF16(IDS_ACCNAME_ZOOM); } + +views::BubbleDelegateView* ZoomView::GetBubble() const { + return ZoomBubbleView::GetZoomBubble(); +}
diff --git a/chrome/browser/ui/views/location_bar/zoom_view.h b/chrome/browser/ui/views/location_bar/zoom_view.h index 3ec315e..1224f8c 100644 --- a/chrome/browser/ui/views/location_bar/zoom_view.h +++ b/chrome/browser/ui/views/location_bar/zoom_view.h
@@ -29,9 +29,9 @@ protected: // BubbleIconView: - bool IsBubbleShowing() const override; void OnExecuting(BubbleIconView::ExecuteSource source) override; void GetAccessibleState(ui::AXViewState* state) override; + views::BubbleDelegateView* GetBubble() const override; private: // The delegate used to get the currently visible WebContents.
diff --git a/chrome/browser/ui/views/managed_full_screen_bubble_delegate_view.cc b/chrome/browser/ui/views/managed_full_screen_bubble_delegate_view.cc index 9a867d2e..04e5cc5 100644 --- a/chrome/browser/ui/views/managed_full_screen_bubble_delegate_view.cc +++ b/chrome/browser/ui/views/managed_full_screen_bubble_delegate_view.cc
@@ -41,7 +41,9 @@ } void ManagedFullScreenBubbleDelegateView::Close() { - GetWidget()->Close(); + views::Widget* widget = GetWidget(); + if (!widget->IsClosed()) + widget->Close(); } void ManagedFullScreenBubbleDelegateView::AdjustForFullscreen(
diff --git a/chrome/browser/ui/views/omnibox/omnibox_result_view.cc b/chrome/browser/ui/views/omnibox/omnibox_result_view.cc index 283f9e4..5111e835 100644 --- a/chrome/browser/ui/views/omnibox/omnibox_result_view.cc +++ b/chrome/browser/ui/views/omnibox/omnibox_result_view.cc
@@ -375,6 +375,9 @@ separator_width_ = separator_rendertext_->GetContentWidth(); } + contents->SetDisplayRect(gfx::Rect(gfx::Size(INT_MAX, 0))); + if (description) + description->SetDisplayRect(gfx::Rect(gfx::Size(INT_MAX, 0))); int contents_max_width, description_max_width; OmniboxPopupModel::ComputeMatchMaxWidths( contents->GetContentWidth(), @@ -544,7 +547,8 @@ int OmniboxResultView::GetMatchContentsWidth() const { InitContentsRenderTextIfNecessary(); - return contents_rendertext_ ? contents_rendertext_->GetContentWidth() : 0; + contents_rendertext_->SetDisplayRect(gfx::Rect(gfx::Size(INT_MAX, 0))); + return contents_rendertext_->GetContentWidth(); } void OmniboxResultView::SetAnswerImage(const gfx::ImageSkia& image) {
diff --git a/chrome/browser/ui/views/omnibox/omnibox_view_views.cc b/chrome/browser/ui/views/omnibox/omnibox_view_views.cc index a2b04d6..b50d8e6 100644 --- a/chrome/browser/ui/views/omnibox/omnibox_view_views.cc +++ b/chrome/browser/ui/views/omnibox/omnibox_view_views.cc
@@ -416,10 +416,8 @@ if (!views::FocusManager::IsTabTraversalKeyEvent(event)) return false; - if (model()->is_keyword_hint() && !event.IsShiftDown()) { - model()->AcceptKeyword(ENTERED_KEYWORD_MODE_VIA_TAB); - return true; - } + if (model()->is_keyword_hint() && !event.IsShiftDown()) + return model()->AcceptKeyword(ENTERED_KEYWORD_MODE_VIA_TAB); if (!model()->popup_model()->IsOpen()) return false; @@ -427,7 +425,7 @@ if (event.IsShiftDown() && (model()->popup_model()->selected_line_state() == OmniboxPopupModel::KEYWORD)) - model()->ClearKeyword(text()); + model()->ClearKeyword(); else model()->OnUpOrDownKeyPressed(event.IsShiftDown() ? -1 : 1); @@ -468,7 +466,8 @@ // Prevent inline autocomplete when the caret isn't at the end of the text. const gfx::Range sel = GetSelectedRange(); - model()->StartAutocomplete(!sel.is_empty(), sel.GetMax() < text().length()); + model()->StartAutocomplete(!sel.is_empty(), sel.GetMax() < text().length(), + false); } void OmniboxViewViews::ApplyCaretVisibility() { @@ -929,7 +928,7 @@ if (model()->is_keyword_hint() || model()->keyword().empty() || HasSelection() || GetCursorPosition() != 0) return false; - model()->ClearKeyword(text()); + model()->ClearKeyword(); return true; }
diff --git a/chrome/browser/ui/views/passwords/manage_passwords_bubble_view.cc b/chrome/browser/ui/views/passwords/manage_passwords_bubble_view.cc index b1bede3a..0daf101f 100644 --- a/chrome/browser/ui/views/passwords/manage_passwords_bubble_view.cc +++ b/chrome/browser/ui/views/passwords/manage_passwords_bubble_view.cc
@@ -1058,9 +1058,8 @@ Browser* browser = chrome::FindBrowserWithWebContents(web_contents); DCHECK(browser); DCHECK(browser->window()); - - if (IsShowing()) - return; + DCHECK(!manage_passwords_bubble_ || + !manage_passwords_bubble_->GetWidget()->IsVisible()); BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser); bool is_fullscreen = browser_view->IsFullscreen(); @@ -1095,18 +1094,11 @@ // static void ManagePasswordsBubbleView::ActivateBubble() { - if (!IsShowing()) - return; + DCHECK(manage_passwords_bubble_); + DCHECK(manage_passwords_bubble_->GetWidget()->IsVisible()); manage_passwords_bubble_->GetWidget()->Activate(); } -// static -bool ManagePasswordsBubbleView::IsShowing() { - // The bubble may be in the process of closing. - return (manage_passwords_bubble_ != NULL) && - manage_passwords_bubble_->GetWidget()->IsVisible(); -} - content::WebContents* ManagePasswordsBubbleView::web_contents() const { return model()->web_contents(); }
diff --git a/chrome/browser/ui/views/passwords/manage_passwords_bubble_view.h b/chrome/browser/ui/views/passwords/manage_passwords_bubble_view.h index 173b995d..01c0ea15 100644 --- a/chrome/browser/ui/views/passwords/manage_passwords_bubble_view.h +++ b/chrome/browser/ui/views/passwords/manage_passwords_bubble_view.h
@@ -35,11 +35,8 @@ // Makes the bubble the foreground window. static void ActivateBubble(); - // Whether the bubble is currently showing. - static bool IsShowing(); - // Returns a pointer to the bubble. - static const ManagePasswordsBubbleView* manage_password_bubble() { + static ManagePasswordsBubbleView* manage_password_bubble() { return manage_passwords_bubble_; }
diff --git a/chrome/browser/ui/views/passwords/manage_passwords_bubble_view_browsertest.cc b/chrome/browser/ui/views/passwords/manage_passwords_bubble_view_browsertest.cc index 654b1d56..2100e804 100644 --- a/chrome/browser/ui/views/passwords/manage_passwords_bubble_view_browsertest.cc +++ b/chrome/browser/ui/views/passwords/manage_passwords_bubble_view_browsertest.cc
@@ -48,6 +48,12 @@ MOCK_METHOD1(OnRequestDone, void(const GURL&)); }; +bool IsBubbleShowing() { + return ManagePasswordsBubbleView::manage_password_bubble() && + ManagePasswordsBubbleView::manage_password_bubble()-> + GetWidget()->IsVisible(); +} + } // namespace namespace metrics_util = password_manager::metrics_util; @@ -69,18 +75,18 @@ }; IN_PROC_BROWSER_TEST_F(ManagePasswordsBubbleViewTest, BasicOpenAndClose) { - EXPECT_FALSE(ManagePasswordsBubbleView::IsShowing()); + EXPECT_FALSE(IsBubbleShowing()); ManagePasswordsBubbleView::ShowBubble( browser()->tab_strip_model()->GetActiveWebContents(), ManagePasswordsBubble::USER_ACTION); - EXPECT_TRUE(ManagePasswordsBubbleView::IsShowing()); + EXPECT_TRUE(IsBubbleShowing()); const ManagePasswordsBubbleView* bubble = ManagePasswordsBubbleView::manage_password_bubble(); EXPECT_TRUE(bubble->initially_focused_view()); EXPECT_EQ(bubble->initially_focused_view(), bubble->GetFocusManager()->GetFocusedView()); ManagePasswordsBubbleView::CloseBubble(); - EXPECT_FALSE(ManagePasswordsBubbleView::IsShowing()); + EXPECT_FALSE(IsBubbleShowing()); // And, just for grins, ensure that we can re-open the bubble. ManagePasswordsBubbleView::ShowBubble( @@ -88,9 +94,9 @@ ManagePasswordsBubble::USER_ACTION); EXPECT_TRUE(ManagePasswordsBubbleView::manage_password_bubble()-> GetFocusManager()->GetFocusedView()); - EXPECT_TRUE(ManagePasswordsBubbleView::IsShowing()); + EXPECT_TRUE(IsBubbleShowing()); ManagePasswordsBubbleView::CloseBubble(); - EXPECT_FALSE(ManagePasswordsBubbleView::IsShowing()); + EXPECT_FALSE(IsBubbleShowing()); } // Same as 'BasicOpenAndClose', but use the command rather than the static @@ -98,30 +104,30 @@ IN_PROC_BROWSER_TEST_F(ManagePasswordsBubbleViewTest, CommandControlsBubble) { // The command only works if the icon is visible, so get into management mode. SetupManagingPasswords(); - EXPECT_FALSE(ManagePasswordsBubbleView::IsShowing()); + EXPECT_FALSE(IsBubbleShowing()); ExecuteManagePasswordsCommand(); - EXPECT_TRUE(ManagePasswordsBubbleView::IsShowing()); + EXPECT_TRUE(IsBubbleShowing()); const ManagePasswordsBubbleView* bubble = ManagePasswordsBubbleView::manage_password_bubble(); EXPECT_TRUE(bubble->initially_focused_view()); EXPECT_EQ(bubble->initially_focused_view(), bubble->GetFocusManager()->GetFocusedView()); ManagePasswordsBubbleView::CloseBubble(); - EXPECT_FALSE(ManagePasswordsBubbleView::IsShowing()); + EXPECT_FALSE(IsBubbleShowing()); // And, just for grins, ensure that we can re-open the bubble. ExecuteManagePasswordsCommand(); - EXPECT_TRUE(ManagePasswordsBubbleView::IsShowing()); + EXPECT_TRUE(IsBubbleShowing()); ManagePasswordsBubbleView::CloseBubble(); - EXPECT_FALSE(ManagePasswordsBubbleView::IsShowing()); + EXPECT_FALSE(IsBubbleShowing()); } IN_PROC_BROWSER_TEST_F(ManagePasswordsBubbleViewTest, CommandExecutionInManagingState) { SetupManagingPasswords(); - EXPECT_FALSE(ManagePasswordsBubbleView::IsShowing()); + EXPECT_FALSE(IsBubbleShowing()); ExecuteManagePasswordsCommand(); - EXPECT_TRUE(ManagePasswordsBubbleView::IsShowing()); + EXPECT_TRUE(IsBubbleShowing()); scoped_ptr<base::HistogramSamples> samples( GetSamples(kDisplayDispositionMetric)); @@ -141,7 +147,7 @@ CommandExecutionInAutomaticState) { // Open with pending password: automagical! SetupPendingPassword(); - EXPECT_TRUE(ManagePasswordsBubbleView::IsShowing()); + EXPECT_TRUE(IsBubbleShowing()); // Bubble should not be focused by default. EXPECT_FALSE(ManagePasswordsBubbleView::manage_password_bubble()-> @@ -168,11 +174,11 @@ CommandExecutionInPendingState) { // Open once with pending password: automagical! SetupPendingPassword(); - EXPECT_TRUE(ManagePasswordsBubbleView::IsShowing()); + EXPECT_TRUE(IsBubbleShowing()); ManagePasswordsBubbleView::CloseBubble(); // This opening should be measured as manual. ExecuteManagePasswordsCommand(); - EXPECT_TRUE(ManagePasswordsBubbleView::IsShowing()); + EXPECT_TRUE(IsBubbleShowing()); scoped_ptr<base::HistogramSamples> samples( GetSamples(kDisplayDispositionMetric)); @@ -191,12 +197,12 @@ IN_PROC_BROWSER_TEST_F(ManagePasswordsBubbleViewTest, CommandExecutionInAutomaticSaveState) { SetupAutomaticPassword(); - EXPECT_TRUE(ManagePasswordsBubbleView::IsShowing()); + EXPECT_TRUE(IsBubbleShowing()); ManagePasswordsBubbleView::CloseBubble(); content::RunAllPendingInMessageLoop(); // Re-opening should count as manual. ExecuteManagePasswordsCommand(); - EXPECT_TRUE(ManagePasswordsBubbleView::IsShowing()); + EXPECT_TRUE(IsBubbleShowing()); scoped_ptr<base::HistogramSamples> samples( GetSamples(kDisplayDispositionMetric)); @@ -216,11 +222,11 @@ ManagePasswordsBubbleView::ShowBubble( browser()->tab_strip_model()->GetActiveWebContents(), ManagePasswordsBubble::AUTOMATIC); - EXPECT_TRUE(ManagePasswordsBubbleView::IsShowing()); + EXPECT_TRUE(IsBubbleShowing()); EXPECT_FALSE(ManagePasswordsBubbleView::manage_password_bubble()-> GetFocusManager()->GetFocusedView()); ui_test_utils::ClickOnView(browser(), VIEW_ID_TAB_CONTAINER); - EXPECT_FALSE(ManagePasswordsBubbleView::IsShowing()); + EXPECT_FALSE(IsBubbleShowing()); } IN_PROC_BROWSER_TEST_F(ManagePasswordsBubbleViewTest, CloseOnKey) { @@ -235,41 +241,41 @@ browser()->tab_strip_model()->GetActiveWebContents(); ManagePasswordsBubbleView::ShowBubble(web_contents, ManagePasswordsBubble::AUTOMATIC); - EXPECT_TRUE(ManagePasswordsBubbleView::IsShowing()); + EXPECT_TRUE(IsBubbleShowing()); EXPECT_FALSE(ManagePasswordsBubbleView::manage_password_bubble()-> GetFocusManager()->GetFocusedView()); EXPECT_TRUE(ui_test_utils::IsViewFocused(browser(), VIEW_ID_TAB_CONTAINER)); EXPECT_TRUE(web_contents->GetRenderViewHost()->IsFocusedElementEditable()); ASSERT_TRUE(ui_test_utils::SendKeyPressSync(browser(), ui::VKEY_K, false, false, false, false)); - EXPECT_FALSE(ManagePasswordsBubbleView::IsShowing()); + EXPECT_FALSE(IsBubbleShowing()); } IN_PROC_BROWSER_TEST_F(ManagePasswordsBubbleViewTest, CloseOnChangedState) { SetupPendingPassword(); - EXPECT_TRUE(ManagePasswordsBubbleView::IsShowing()); + EXPECT_TRUE(IsBubbleShowing()); // User navigated very fast and landed on another page with an autofilled // password. The save password bubble should disappear. SetupManagingPasswords(); - EXPECT_FALSE(ManagePasswordsBubbleView::IsShowing()); + EXPECT_FALSE(IsBubbleShowing()); } IN_PROC_BROWSER_TEST_F(ManagePasswordsBubbleViewTest, TwoTabsWithBubble) { // Set up the first tab with the bubble. SetupPendingPassword(); - EXPECT_TRUE(ManagePasswordsBubbleView::IsShowing()); + EXPECT_TRUE(IsBubbleShowing()); // Set up the second tab. AddTabAtIndex(0, GURL("chrome://newtab"), ui::PAGE_TRANSITION_TYPED); - EXPECT_FALSE(ManagePasswordsBubbleView::IsShowing()); + EXPECT_FALSE(IsBubbleShowing()); ManagePasswordsBubbleView::ShowBubble( browser()->tab_strip_model()->GetActiveWebContents(), ManagePasswordsBubble::AUTOMATIC); - EXPECT_TRUE(ManagePasswordsBubbleView::IsShowing()); + EXPECT_TRUE(IsBubbleShowing()); TabStripModel* tab_model = browser()->tab_strip_model(); EXPECT_EQ(0, tab_model->active_index()); // Back to the first tab. tab_model->ActivateTabAt(1, true); - EXPECT_FALSE(ManagePasswordsBubbleView::IsShowing()); + EXPECT_FALSE(IsBubbleShowing()); } IN_PROC_BROWSER_TEST_F(ManagePasswordsBubbleViewTest, ChooseCredential) { @@ -299,7 +305,7 @@ SetupChooseCredentials(local_credentials.Pass(), federated_credentials.Pass(), origin); - EXPECT_TRUE(ManagePasswordsBubbleView::IsShowing()); + EXPECT_TRUE(IsBubbleShowing()); EXPECT_CALL(*this, OnChooseCredential( Field(&password_manager::CredentialInfo::type, password_manager::CredentialType::CREDENTIAL_TYPE_EMPTY))); @@ -323,7 +329,7 @@ EXPECT_FALSE(browser()->window()->IsActive()); SetupChooseCredentials(local_credentials.Pass(), federated_credentials.Pass(), origin); - EXPECT_TRUE(ManagePasswordsBubbleView::IsShowing()); + EXPECT_TRUE(IsBubbleShowing()); } IN_PROC_BROWSER_TEST_F(ManagePasswordsBubbleViewTest, AutoSignin) { @@ -349,11 +355,11 @@ EXPECT_CALL(url_callback, OnRequestDone(avatar_url)); SetupAutoSignin(local_credentials.Pass()); - EXPECT_TRUE(ManagePasswordsBubbleView::IsShowing()); + EXPECT_TRUE(IsBubbleShowing()); ::testing::Mock::VerifyAndClearExpectations(&url_callback); ManagePasswordsBubbleView::CloseBubble(); - EXPECT_FALSE(ManagePasswordsBubbleView::IsShowing()); + EXPECT_FALSE(IsBubbleShowing()); content::RunAllPendingInMessageLoop(); // Open the bubble to manage accounts. @@ -362,7 +368,7 @@ ManagePasswordsBubbleView::ShowBubble( browser()->tab_strip_model()->GetActiveWebContents(), ManagePasswordsBubble::USER_ACTION); - EXPECT_TRUE(ManagePasswordsBubbleView::IsShowing()); + EXPECT_TRUE(IsBubbleShowing()); } IN_PROC_BROWSER_TEST_F(ManagePasswordsBubbleViewTest, AutoSigninNoFocus) { @@ -381,12 +387,12 @@ ManagePasswordsBubbleView::set_auto_signin_toast_timeout(0); SetupAutoSignin(local_credentials.Pass()); content::RunAllPendingInMessageLoop(); - EXPECT_TRUE(ManagePasswordsBubbleView::IsShowing()); + EXPECT_TRUE(IsBubbleShowing()); // Bring the first window back. The toast closes by timeout. focused_window->window()->Close(); browser()->window()->Activate(); content::RunAllPendingInMessageLoop(); EXPECT_TRUE(browser()->window()->IsActive()); - EXPECT_FALSE(ManagePasswordsBubbleView::IsShowing()); + EXPECT_FALSE(IsBubbleShowing()); }
diff --git a/chrome/browser/ui/views/passwords/manage_passwords_icon_view.cc b/chrome/browser/ui/views/passwords/manage_passwords_icon_view.cc index a0a54b1e..2527a88 100644 --- a/chrome/browser/ui/views/passwords/manage_passwords_icon_view.cc +++ b/chrome/browser/ui/views/passwords/manage_passwords_icon_view.cc
@@ -42,12 +42,6 @@ ManagePasswordsBubbleView::CloseBubble(); } -bool ManagePasswordsIconView::IsBubbleShowing() const { - // If the bubble is being destroyed, it's considered showing though it may be - // already invisible currently. - return ManagePasswordsBubbleView::manage_password_bubble() != NULL; -} - void ManagePasswordsIconView::OnExecuting( BubbleIconView::ExecuteSource source) { } @@ -78,3 +72,7 @@ if (active()) ManagePasswordsBubbleView::ActivateBubble(); } + +views::BubbleDelegateView* ManagePasswordsIconView::GetBubble() const { + return ManagePasswordsBubbleView::manage_password_bubble(); +}
diff --git a/chrome/browser/ui/views/passwords/manage_passwords_icon_view.h b/chrome/browser/ui/views/passwords/manage_passwords_icon_view.h index 2d0c3d0..0b901c1 100644 --- a/chrome/browser/ui/views/passwords/manage_passwords_icon_view.h +++ b/chrome/browser/ui/views/passwords/manage_passwords_icon_view.h
@@ -22,7 +22,6 @@ ~ManagePasswordsIconView() override; // BubbleIconView: - bool IsBubbleShowing() const override; void OnExecuting(BubbleIconView::ExecuteSource source) override; bool OnMousePressed(const ui::MouseEvent& event) override; bool OnKeyPressed(const ui::KeyEvent& event) override; @@ -40,6 +39,9 @@ void UpdateVisibleUI() override; void OnChangingState() override; + // BubbleIconView: + views::BubbleDelegateView* GetBubble() const override; + private: DISALLOW_COPY_AND_ASSIGN(ManagePasswordsIconView);
diff --git a/chrome/browser/ui/views/tab_dialogs_views.cc b/chrome/browser/ui/views/tab_dialogs_views.cc index f235dce..517764d9 100644 --- a/chrome/browser/ui/views/tab_dialogs_views.cc +++ b/chrome/browser/ui/views/tab_dialogs_views.cc
@@ -53,7 +53,7 @@ } void TabDialogsViews::ShowManagePasswordsBubble(bool user_action) { - if (ManagePasswordsBubbleView::IsShowing()) { + if (ManagePasswordsBubbleView::manage_password_bubble()) { // The bubble is currently shown for some other tab. We should close it now // and open for |web_contents_|. ManagePasswordsBubbleView::CloseBubble(); @@ -64,7 +64,7 @@ } void TabDialogsViews::HideManagePasswordsBubble() { - if (!ManagePasswordsBubbleView::IsShowing()) + if (!ManagePasswordsBubbleView::manage_password_bubble()) return; content::WebContents* bubble_web_contents = ManagePasswordsBubbleView::manage_password_bubble()->web_contents();
diff --git a/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc b/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc index 9be153ab..3e505ff 100644 --- a/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc +++ b/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc
@@ -17,6 +17,7 @@ #include "chrome/browser/ui/browser_commands.h" #include "chrome/browser/ui/browser_iterator.h" #include "chrome/browser/ui/browser_list.h" +#include "chrome/browser/ui/browser_tabstrip.h" #include "chrome/browser/ui/host_desktop.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" #include "chrome/browser/ui/views/frame/browser_view.h" @@ -1325,8 +1326,14 @@ ASSERT_TRUE(TabDragController::IsActive()); ASSERT_EQ(2u, browser_list->size()); - // Add another tab. This should trigger exiting the nested loop. - test->AddBlankTabAndShow(browser_list->GetLastActive()); + // Add another tab. This should trigger exiting the nested loop. Add at the + // to exercise past crash when model/tabstrip got out of sync (474082). + content::WindowedNotificationObserver observer( + content::NOTIFICATION_LOAD_STOP, + content::NotificationService::AllSources()); + chrome::AddTabAt(browser_list->GetLastActive(), GURL(url::kAboutBlankURL), + 0, false); + observer.Wait(); } } // namespace
diff --git a/chrome/browser/ui/views/tabs/tab_strip.cc b/chrome/browser/ui/views/tabs/tab_strip.cc index 816f023..b01e196 100644 --- a/chrome/browser/ui/views/tabs/tab_strip.cc +++ b/chrome/browser/ui/views/tabs/tab_strip.cc
@@ -630,14 +630,6 @@ void TabStrip::AddTabAt(int model_index, const TabRendererData& data, bool is_active) { - // Stop dragging when a new tab is added and dragging a window. Doing - // otherwise results in a confusing state if the user attempts to reattach. We - // could allow this and make TabDragController update itself during the add, - // but this comes up infrequently enough that it's not work the complexity. - if (drag_controller_.get() && !drag_controller_->is_mutating() && - drag_controller_->is_dragging_window()) { - EndDrag(END_DRAG_COMPLETE); - } Tab* tab = CreateTab(); tab->SetData(data); UpdateTabsClosingMap(model_index, 1); @@ -665,6 +657,20 @@ FOR_EACH_OBSERVER(TabStripObserver, observers_, TabStripAddedTabAt(this, model_index)); + + // Stop dragging when a new tab is added and dragging a window. Doing + // otherwise results in a confusing state if the user attempts to reattach. We + // could allow this and make TabDragController update itself during the add, + // but this comes up infrequently enough that it's not worth the complexity. + // + // At the start of AddTabAt() the model and tabs are out sync. Any queries to + // find a tab given a model index can go off the end of |tabs_|. As such, it + // is important that we complete the drag *after* adding the tab so that the + // model and tabstrip are in sync. + if (drag_controller_.get() && !drag_controller_->is_mutating() && + drag_controller_->is_dragging_window()) { + EndDrag(END_DRAG_COMPLETE); + } } void TabStrip::MoveTab(int from_model_index,
diff --git a/chrome/browser/ui/views/toolbar/browser_actions_container_browsertest.cc b/chrome/browser/ui/views/toolbar/browser_actions_container_browsertest.cc index 1c8f05ec..5373da2 100644 --- a/chrome/browser/ui/views/toolbar/browser_actions_container_browsertest.cc +++ b/chrome/browser/ui/views/toolbar/browser_actions_container_browsertest.cc
@@ -351,8 +351,7 @@ // Hide action A. This results in it being sent to overflow, and reducing the // visible size to 1, so the order should be C A B, with only C visible in the // main bar. - extensions::ExtensionActionAPI::SetBrowserActionVisibility( - extensions::ExtensionPrefs::Get(profile()), + extensions::ExtensionActionAPI::Get(profile())->SetBrowserActionVisibility( extension_a()->id(), false); overflow_bar()->Layout(); // Kick.
diff --git a/chrome/browser/ui/views/translate/translate_bubble_view.cc b/chrome/browser/ui/views/translate/translate_bubble_view.cc index 145d4443..d86cd49 100644 --- a/chrome/browser/ui/views/translate/translate_bubble_view.cc +++ b/chrome/browser/ui/views/translate/translate_bubble_view.cc
@@ -85,7 +85,7 @@ translate::TranslateStep step, translate::TranslateErrors::Type error_type, bool is_user_gesture) { - if (IsShowing()) { + if (translate_bubble_view_) { // When the user reads the advanced setting panel, the bubble should not be // changed because he/she is focusing on the bubble. if (translate_bubble_view_->web_contents() == web_contents && @@ -133,18 +133,13 @@ // static void TranslateBubbleView::CloseBubble() { - if (!IsShowing()) + if (!translate_bubble_view_) return; translate_bubble_view_->GetWidget()->Close(); } // static -bool TranslateBubbleView::IsShowing() { - return translate_bubble_view_ != NULL; -} - -// static TranslateBubbleView* TranslateBubbleView::GetCurrentBubble() { return translate_bubble_view_; }
diff --git a/chrome/browser/ui/views/translate/translate_bubble_view.h b/chrome/browser/ui/views/translate/translate_bubble_view.h index 014aa4d..61506e8 100644 --- a/chrome/browser/ui/views/translate/translate_bubble_view.h +++ b/chrome/browser/ui/views/translate/translate_bubble_view.h
@@ -54,9 +54,6 @@ // Closes the current bubble if existing. static void CloseBubble(); - // If true, the Translate bubble is being shown. - static bool IsShowing(); - // Returns the bubble view currently shown. This may return NULL. static TranslateBubbleView* GetCurrentBubble();
diff --git a/chrome/browser/ui/views/translate/translate_bubble_view_browsertest.cc b/chrome/browser/ui/views/translate/translate_bubble_view_browsertest.cc index fa856e43..1c0e41b6c 100644 --- a/chrome/browser/ui/views/translate/translate_bubble_view_browsertest.cc +++ b/chrome/browser/ui/views/translate/translate_bubble_view_browsertest.cc
@@ -40,7 +40,7 @@ // Flaky: crbug.com/394066 IN_PROC_BROWSER_TEST_F(TranslateBubbleViewBrowserTest, DISABLED_CloseBrowserWithoutTranslating) { - EXPECT_FALSE(TranslateBubbleView::IsShowing()); + EXPECT_FALSE(TranslateBubbleView::GetCurrentBubble()); // Show a French page and wait until the bubble is shown. content::WebContents* current_web_contents = @@ -54,17 +54,17 @@ base::FilePath(), base::FilePath(FILE_PATH_LITERAL("french_page.html"))); ui_test_utils::NavigateToURL(browser(), french_url); fr_language_detected_signal.Wait(); - EXPECT_TRUE(TranslateBubbleView::IsShowing()); + EXPECT_TRUE(TranslateBubbleView::GetCurrentBubble()); // Close the window without translating. chrome::CloseWindow(browser()); - EXPECT_FALSE(TranslateBubbleView::IsShowing()); + EXPECT_FALSE(TranslateBubbleView::GetCurrentBubble()); } // http://crbug.com/378061 IN_PROC_BROWSER_TEST_F(TranslateBubbleViewBrowserTest, DISABLED_CloseLastTabWithoutTranslating) { - EXPECT_FALSE(TranslateBubbleView::IsShowing()); + EXPECT_FALSE(TranslateBubbleView::GetCurrentBubble()); // Show a French page and wait until the bubble is shown. content::WebContents* current_web_contents = @@ -78,17 +78,17 @@ base::FilePath(), base::FilePath(FILE_PATH_LITERAL("french_page.html"))); ui_test_utils::NavigateToURL(browser(), french_url); fr_language_detected_signal.Wait(); - EXPECT_TRUE(TranslateBubbleView::IsShowing()); + EXPECT_TRUE(TranslateBubbleView::GetCurrentBubble()); // Close the tab without translating. EXPECT_EQ(1, browser()->tab_strip_model()->count()); chrome::CloseWebContents(browser(), current_web_contents, false); - EXPECT_FALSE(TranslateBubbleView::IsShowing()); + EXPECT_FALSE(TranslateBubbleView::GetCurrentBubble()); } IN_PROC_BROWSER_TEST_F(TranslateBubbleViewBrowserTest, CloseAnotherTabWithoutTranslating) { - EXPECT_FALSE(TranslateBubbleView::IsShowing()); + EXPECT_FALSE(TranslateBubbleView::GetCurrentBubble()); int active_index = browser()->tab_strip_model()->active_index(); @@ -111,13 +111,13 @@ fr_language_detected_signal.Wait(); // The bubble is not shown because the tab is not activated. - EXPECT_FALSE(TranslateBubbleView::IsShowing()); + EXPECT_FALSE(TranslateBubbleView::GetCurrentBubble()); // Close the French page tab immediately. chrome::CloseWebContents(browser(), web_contents, false); EXPECT_EQ(active_index, browser()->tab_strip_model()->active_index()); EXPECT_EQ(1, browser()->tab_strip_model()->count()); - EXPECT_FALSE(TranslateBubbleView::IsShowing()); + EXPECT_FALSE(TranslateBubbleView::GetCurrentBubble()); // Close the last tab. chrome::CloseWebContents(browser(),
diff --git a/chrome/browser/ui/webui/certificate_viewer_webui.cc b/chrome/browser/ui/webui/certificate_viewer_webui.cc index 210ff38..a7f9b60 100644 --- a/chrome/browser/ui/webui/certificate_viewer_webui.cc +++ b/chrome/browser/ui/webui/certificate_viewer_webui.cc
@@ -204,8 +204,7 @@ void CertificateViewerModalDialog::OnCloseContents(WebContents* source, bool* out_close_dialog) { - if (out_close_dialog) - *out_close_dialog = true; + *out_close_dialog = true; } bool CertificateViewerModalDialog::ShouldShowDialogTitle() const {
diff --git a/chrome/browser/ui/webui/chromeos/login/network_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/network_screen_handler.cc index 2f0caa60..01673ca 100644 --- a/chrome/browser/ui/webui/chromeos/login/network_screen_handler.cc +++ b/chrome/browser/ui/webui/chromeos/login/network_screen_handler.cc
@@ -4,6 +4,7 @@ #include "chrome/browser/ui/webui/chromeos/login/network_screen_handler.h" +#include "ash/system/system_notifier.h" #include "base/bind.h" #include "base/bind_helpers.h" #include "base/command_line.h" @@ -19,9 +20,11 @@ #include "chrome/browser/chromeos/idle_detector.h" #include "chrome/browser/chromeos/login/screens/core_oobe_actor.h" #include "chrome/browser/chromeos/login/screens/network_model.h" +#include "chrome/browser/chromeos/login/startup_utils.h" #include "chrome/browser/chromeos/login/ui/input_events_blocker.h" #include "chrome/browser/chromeos/system/input_device_settings.h" #include "chrome/browser/chromeos/system/timezone_util.h" +#include "chrome/browser/lifetime/application_lifetime.h" #include "chrome/browser/ui/webui/chromeos/login/l10n_util.h" #include "chrome/browser/ui/webui/chromeos/login/oobe_ui.h" #include "chrome/common/pref_names.h" @@ -32,14 +35,41 @@ #include "components/login/localized_values_builder.h" #include "components/user_manager/user_manager.h" #include "content/public/browser/browser_thread.h" +#include "grit/ash_strings.h" #include "ui/base/ime/chromeos/extension_ime_util.h" #include "ui/base/l10n/l10n_util.h" #include "ui/gfx/geometry/rect.h" +#include "ui/message_center/message_center.h" +#include "ui/message_center/notification.h" #include "ui/views/layout/fill_layout.h" #include "ui/views/widget/widget.h" namespace { + const char kJsScreenPath[] = "login.NetworkScreen"; +const char kNewGAIAKillSwitch[] = "new_gaia_kill_switch"; + +void ShowNewLoginUIPopup() { + // Show new login UI popup message, if necessary + if (g_browser_process->local_state()->GetBoolean(prefs::kNewLoginUIPopup)) { + base::string16 message = l10n_util::GetStringUTF16( + chromeos::StartupUtils::IsWebviewSigninEnabled() + ? IDS_ASH_STATUS_TRAY_NEW_LOGIN_UI_ENABLED + : IDS_ASH_STATUS_TRAY_NEW_LOGIN_UI_DISABLED); + scoped_ptr<message_center::Notification> notification( + new message_center::Notification( + message_center::NOTIFICATION_TYPE_SIMPLE, kNewGAIAKillSwitch, + base::string16(), message, gfx::Image(), base::string16(), + message_center::NotifierId( + message_center::NotifierId::SYSTEM_COMPONENT, + ash::system_notifier::kNotifierOobeScreen), + message_center::RichNotificationData(), nullptr)); + message_center::MessageCenter::Get()->AddNotification(notification.Pass()); + g_browser_process->local_state()->SetBoolean(prefs::kNewLoginUIPopup, + false); + } +} + } // namespace namespace chromeos { @@ -96,6 +126,8 @@ chromeos::switches::kSystemDevMode)); ShowScreen(OobeUI::kScreenOobeNetwork, &network_screen_params); core_oobe_actor_->InitDemoModeDetection(); + + ShowNewLoginUIPopup(); } void NetworkScreenHandler::Hide() { @@ -138,6 +170,12 @@ // NetworkScreenHandler, BaseScreenHandler implementation: -------------------- +void NetworkScreenHandler::RegisterMessages() { + AddCallback("toggleNewLoginUI", + &NetworkScreenHandler::HandleToggleNewLoginUI); + BaseScreenHandler::RegisterMessages(); +} + void NetworkScreenHandler::DeclareLocalizedValues( ::login::LocalizedValuesBuilder* builder) { if (system::InputDeviceSettings::Get()->ForceKeyboardDrivenUINavigation()) @@ -222,6 +260,14 @@ ReloadLocalizedContent(); } +void NetworkScreenHandler::HandleToggleNewLoginUI() { + if (StartupUtils::EnableWebviewSignin( + !StartupUtils::IsWebviewSigninEnabled())) { + g_browser_process->local_state()->SetBoolean(prefs::kNewLoginUIPopup, true); + chrome::AttemptRestart(); + } +} + // NetworkScreenHandler, private: ---------------------------------------------- // static
diff --git a/chrome/browser/ui/webui/chromeos/login/network_screen_handler.h b/chrome/browser/ui/webui/chromeos/login/network_screen_handler.h index 78813817d..32b4cf4 100644 --- a/chrome/browser/ui/webui/chromeos/login/network_screen_handler.h +++ b/chrome/browser/ui/webui/chromeos/login/network_screen_handler.h
@@ -44,11 +44,15 @@ void ReloadLocalizedContent() override; // BaseScreenHandler implementation: + void RegisterMessages() override; void DeclareLocalizedValues( ::login::LocalizedValuesBuilder* builder) override; void GetAdditionalParameters(base::DictionaryValue* dict) override; void Initialize() override; + // WebUI message handlers. + void HandleToggleNewLoginUI(); + private: // Returns available timezones. Caller gets the ownership. static base::ListValue* GetTimezoneList();
diff --git a/chrome/browser/ui/webui/chromeos/login/oobe_ui.cc b/chrome/browser/ui/webui/chromeos/login/oobe_ui.cc index 2f3a1cb..b271920 100644 --- a/chrome/browser/ui/webui/chromeos/login/oobe_ui.cc +++ b/chrome/browser/ui/webui/chromeos/login/oobe_ui.cc
@@ -294,18 +294,8 @@ error_screen_handler_ = new ErrorScreenHandler(); AddScreenHandler(error_screen_handler_); - // Initialize ErrorScreen if it hasn't initialized so that NetworkErrorModel - // is binded properly. - NetworkErrorModel* network_error_model = nullptr; - if (WizardController::default_controller()) { - network_error_model = static_cast<NetworkErrorModel*>( - WizardController::default_controller()->GetScreen( - WizardController::kErrorScreenName)); - CHECK(network_error_model); - } else { - error_screen_.reset(new ErrorScreen(nullptr, error_screen_handler_)); - network_error_model = error_screen_.get(); - } + error_screen_.reset(new ErrorScreen(nullptr, error_screen_handler_)); + NetworkErrorModel* network_error_model = error_screen_.get(); EnrollmentScreenHandler* enrollment_screen_handler = new EnrollmentScreenHandler(network_state_informer_, network_error_model); @@ -468,8 +458,8 @@ return user_image_view_; } -NetworkErrorView* OobeUI::GetNetworkErrorView() { - return error_screen_handler_; +ErrorScreen* OobeUI::GetErrorScreen() { + return error_screen_.get(); } SupervisedUserCreationScreenHandler* @@ -527,10 +517,6 @@ localized_strings->SetString("newKioskUI", new_kiosk_ui ? "on" : "off"); } -scoped_ptr<ErrorScreen> OobeUI::GetErrorScreen() { - return error_screen_.Pass(); -} - void OobeUI::InitializeScreenMaps() { screen_names_.resize(SCREEN_UNKNOWN); screen_names_[SCREEN_OOBE_HID_DETECTION] = kScreenOobeHIDDetection;
diff --git a/chrome/browser/ui/webui/chromeos/login/oobe_ui.h b/chrome/browser/ui/webui/chromeos/login/oobe_ui.h index 59a9a3a..45b05aa 100644 --- a/chrome/browser/ui/webui/chromeos/login/oobe_ui.h +++ b/chrome/browser/ui/webui/chromeos/login/oobe_ui.h
@@ -110,7 +110,7 @@ KioskEnableScreenActor* GetKioskEnableScreenActor() override; TermsOfServiceScreenActor* GetTermsOfServiceScreenActor() override; UserImageView* GetUserImageView() override; - NetworkErrorView* GetNetworkErrorView() override; + ErrorScreen* GetErrorScreen() override; WrongHWIDScreenActor* GetWrongHWIDScreenActor() override; AutoEnrollmentCheckScreenActor* GetAutoEnrollmentCheckScreenActor() override; SupervisedUserCreationScreenHandler* GetSupervisedUserCreationScreenActor() @@ -168,11 +168,6 @@ return network_state_informer_.get(); } - // If an error screen was created during initialization, then it may be - // fetched using this method, while also passing the ownership of - // |error_screen_|. - scoped_ptr<ErrorScreen> GetErrorScreen(); - private: // Initializes |screen_ids_| and |screen_names_| structures. void InitializeScreenMaps();
diff --git a/chrome/browser/ui/webui/chromeos/login/supervised_user_creation_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/supervised_user_creation_screen_handler.cc index 8efe307..4a015d78 100644 --- a/chrome/browser/ui/webui/chromeos/login/supervised_user_creation_screen_handler.cc +++ b/chrome/browser/ui/webui/chromeos/login/supervised_user_creation_screen_handler.cc
@@ -165,6 +165,9 @@ AddCallback("abortLocalSupervisedUserCreation", &SupervisedUserCreationScreenHandler:: HandleAbortLocalSupervisedUserCreation); + AddCallback("hideLocalSupervisedUserCreation", + &SupervisedUserCreationScreenHandler:: + HandleHideLocalSupervisedUserCreation); AddCallback("checkSupervisedUserName", &SupervisedUserCreationScreenHandler:: HandleCheckSupervisedUserName); @@ -286,6 +289,11 @@ delegate_->AbortFlow(); } +void SupervisedUserCreationScreenHandler:: + HandleHideLocalSupervisedUserCreation() { + delegate_->HideFlow(); +} + void SupervisedUserCreationScreenHandler::HandleManagerSelected( const std::string& manager_id) { if (!delegate_)
diff --git a/chrome/browser/ui/webui/chromeos/login/supervised_user_creation_screen_handler.h b/chrome/browser/ui/webui/chromeos/login/supervised_user_creation_screen_handler.h index 1395d5b..4d9284be8 100644 --- a/chrome/browser/ui/webui/chromeos/login/supervised_user_creation_screen_handler.h +++ b/chrome/browser/ui/webui/chromeos/login/supervised_user_creation_screen_handler.h
@@ -56,6 +56,7 @@ virtual void AbortFlow() = 0; virtual void FinishFlow() = 0; + virtual void HideFlow() = 0; virtual void OnPhotoTaken(const std::string& raw_data) = 0; virtual void OnImageSelected(const std::string& image_url, @@ -110,6 +111,7 @@ void HandleFinishLocalSupervisedUserCreation(); void HandleAbortLocalSupervisedUserCreation(); + void HandleHideLocalSupervisedUserCreation(); void HandleRetryLocalSupervisedUserCreation(const base::ListValue* args); void HandleCurrentSupervisedUserPage(const std::string& current_page);
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 d8a0567..b1d3d56 100644 --- a/chrome/browser/ui/webui/extensions/chromeos/kiosk_apps_handler.cc +++ b/chrome/browser/ui/webui/extensions/chromeos/kiosk_apps_handler.cc
@@ -15,6 +15,7 @@ #include "base/strings/string_util.h" #include "base/sys_info.h" #include "base/values.h" +#include "chrome/browser/chromeos/ownership/owner_settings_service_chromeos.h" #include "chrome/browser/chromeos/settings/cros_settings.h" #include "chrome/grit/chromium_strings.h" #include "chrome/grit/generated_resources.h" @@ -94,11 +95,12 @@ } // namespace -KioskAppsHandler::KioskAppsHandler() +KioskAppsHandler::KioskAppsHandler(OwnerSettingsServiceChromeOS* service) : kiosk_app_manager_(KioskAppManager::Get()), initialized_(false), is_kiosk_enabled_(false), is_auto_launch_enabled_(false), + owner_settings_service_(service), weak_ptr_factory_(this) { kiosk_app_manager_->AddObserver(this); } @@ -281,7 +283,7 @@ return; } - kiosk_app_manager_->AddApp(app_id); + kiosk_app_manager_->AddApp(app_id, owner_settings_service_); } void KioskAppsHandler::HandleRemoveKioskApp(const base::ListValue* args) { @@ -291,7 +293,7 @@ std::string app_id; CHECK(args->GetString(0, &app_id)); - kiosk_app_manager_->RemoveApp(app_id); + kiosk_app_manager_->RemoveApp(app_id, owner_settings_service_); } void KioskAppsHandler::HandleEnableKioskAutoLaunch( @@ -302,7 +304,7 @@ std::string app_id; CHECK(args->GetString(0, &app_id)); - kiosk_app_manager_->SetAutoLaunchApp(app_id); + kiosk_app_manager_->SetAutoLaunchApp(app_id, owner_settings_service_); } void KioskAppsHandler::HandleDisableKioskAutoLaunch( @@ -317,7 +319,7 @@ if (startup_app_id != app_id) return; - kiosk_app_manager_->SetAutoLaunchApp(""); + kiosk_app_manager_->SetAutoLaunchApp("", owner_settings_service_); } void KioskAppsHandler::HandleSetDisableBailoutShortcut( @@ -328,7 +330,7 @@ bool disable_bailout_shortcut; CHECK(args->GetBoolean(0, &disable_bailout_shortcut)); - CrosSettings::Get()->SetBoolean( + owner_settings_service_->SetBoolean( kAccountsPrefDeviceLocalAccountAutoLoginBailoutEnabled, !disable_bailout_shortcut); } @@ -350,7 +352,7 @@ web_ui()->CallJavascriptFunction("extensions.KioskAppsOverlay.showError", app_id_value); - kiosk_app_manager_->RemoveApp(app_id); + kiosk_app_manager_->RemoveApp(app_id, owner_settings_service_); } } // namespace chromeos
diff --git a/chrome/browser/ui/webui/extensions/chromeos/kiosk_apps_handler.h b/chrome/browser/ui/webui/extensions/chromeos/kiosk_apps_handler.h index 23df3a1450..83fa88e 100644 --- a/chrome/browser/ui/webui/extensions/chromeos/kiosk_apps_handler.h +++ b/chrome/browser/ui/webui/extensions/chromeos/kiosk_apps_handler.h
@@ -25,11 +25,12 @@ namespace chromeos { class KioskAppManager; +class OwnerSettingsServiceChromeOS; class KioskAppsHandler : public content::WebUIMessageHandler, public KioskAppManagerObserver { public: - KioskAppsHandler(); + explicit KioskAppsHandler(OwnerSettingsServiceChromeOS* service); ~KioskAppsHandler() override; void GetLocalizedValues(content::WebUIDataSource* source); @@ -69,6 +70,7 @@ bool initialized_; bool is_kiosk_enabled_; bool is_auto_launch_enabled_; + OwnerSettingsServiceChromeOS* const owner_settings_service_; // not owned base::WeakPtrFactory<KioskAppsHandler> weak_ptr_factory_; DISALLOW_COPY_AND_ASSIGN(KioskAppsHandler);
diff --git a/chrome/browser/ui/webui/extensions/extension_settings_handler.cc b/chrome/browser/ui/webui/extensions/extension_settings_handler.cc index 0ba6bc2c..dc6f1958 100644 --- a/chrome/browser/ui/webui/extensions/extension_settings_handler.cc +++ b/chrome/browser/ui/webui/extensions/extension_settings_handler.cc
@@ -35,9 +35,6 @@ #include "chrome/grit/generated_resources.h" #include "components/google/core/browser/google_util.h" #include "components/pref_registry/pref_registry_syncable.h" -#include "content/public/browser/notification_service.h" -#include "content/public/browser/notification_source.h" -#include "content/public/browser/notification_types.h" #include "content/public/browser/render_process_host.h" #include "content/public/browser/render_view_host.h" #include "content/public/browser/site_instance.h" @@ -309,16 +306,6 @@ AsWeakPtr())); } -void ExtensionSettingsHandler::Observe( - int type, - const content::NotificationSource& source, - const content::NotificationDetails& details) { - DCHECK_EQ( - extensions::NOTIFICATION_EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED, - type); - MaybeUpdateAfterNotification(); -} - void ExtensionSettingsHandler::OnExtensionDisableReasonsChanged( const std::string& extension_id, int disable_reasons) { MaybeUpdateAfterNotification(); @@ -350,17 +337,12 @@ void ExtensionSettingsHandler::HandleRegisterMessage( const base::ListValue* args) { - if (!registrar_.IsEmpty()) + if (content::WebContentsObserver::web_contents()) return; // Only register once. - Profile* profile = Profile::FromWebUI(web_ui()); - registrar_.Add( - this, - extensions::NOTIFICATION_EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED, - content::Source<ExtensionPrefs>(ExtensionPrefs::Get(profile))); - content::WebContentsObserver::Observe(web_ui()->GetWebContents()); + Profile* profile = Profile::FromWebUI(web_ui()); warning_service_observer_.Add(WarningService::Get(profile)); extension_management_observer_.Add( ExtensionManagementFactory::GetForBrowserContext(profile));
diff --git a/chrome/browser/ui/webui/extensions/extension_settings_handler.h b/chrome/browser/ui/webui/extensions/extension_settings_handler.h index 1e7a95b..69793c0 100644 --- a/chrome/browser/ui/webui/extensions/extension_settings_handler.h +++ b/chrome/browser/ui/webui/extensions/extension_settings_handler.h
@@ -11,8 +11,6 @@ #include "base/scoped_observer.h" #include "chrome/browser/extensions/extension_management.h" #include "content/public/browser/navigation_controller.h" -#include "content/public/browser/notification_observer.h" -#include "content/public/browser/notification_registrar.h" #include "content/public/browser/web_contents_observer.h" #include "content/public/browser/web_ui_message_handler.h" #include "extensions/browser/extension_prefs_observer.h" @@ -41,7 +39,6 @@ // Extension Settings UI handler. class ExtensionSettingsHandler : public content::WebUIMessageHandler, - public content::NotificationObserver, public content::WebContentsObserver, public ExtensionManagement::Observer, public ExtensionPrefsObserver, @@ -65,11 +62,6 @@ // WebUIMessageHandler implementation. void RegisterMessages() override; - // content::NotificationObserver implementation. - void Observe(int type, - const content::NotificationSource& source, - const content::NotificationDetails& details) override; - // ExtensionPrefsObserver implementation. void OnExtensionDisableReasonsChanged(const std::string& extension_id, int disable_reasons) override; @@ -92,8 +84,6 @@ // Our model. Outlives us since it's owned by our containing profile. ExtensionService* extension_service_; - content::NotificationRegistrar registrar_; - ScopedObserver<WarningService, WarningService::Observer> warning_service_observer_;
diff --git a/chrome/browser/ui/webui/extensions/extensions_ui.cc b/chrome/browser/ui/webui/extensions/extensions_ui.cc index 49d921b..681fd3f 100644 --- a/chrome/browser/ui/webui/extensions/extensions_ui.cc +++ b/chrome/browser/ui/webui/extensions/extensions_ui.cc
@@ -18,6 +18,7 @@ #include "ui/base/resource/resource_bundle.h" #if defined(OS_CHROMEOS) +#include "chrome/browser/chromeos/ownership/owner_settings_service_chromeos_factory.h" #include "chrome/browser/ui/webui/extensions/chromeos/kiosk_apps_handler.h" #endif @@ -65,7 +66,9 @@ #if defined(OS_CHROMEOS) chromeos::KioskAppsHandler* kiosk_app_handler = - new chromeos::KioskAppsHandler(); + new chromeos::KioskAppsHandler( + chromeos::OwnerSettingsServiceChromeOSFactory::GetForBrowserContext( + profile)); kiosk_app_handler->GetLocalizedValues(source); web_ui->AddMessageHandler(kiosk_app_handler); #endif
diff --git a/chrome/browser/ui/webui/large_icon_source.cc b/chrome/browser/ui/webui/large_icon_source.cc index df1e285f..6c74b0ef 100644 --- a/chrome/browser/ui/webui/large_icon_source.cc +++ b/chrome/browser/ui/webui/large_icon_source.cc
@@ -4,53 +4,29 @@ #include "chrome/browser/ui/webui/large_icon_source.h" +#include <vector> + #include "base/memory/ref_counted_memory.h" #include "chrome/browser/search/instant_io_context.h" #include "chrome/common/favicon/large_icon_url_parser.h" #include "chrome/common/url_constants.h" #include "components/favicon/core/fallback_icon_service.h" -#include "components/favicon/core/favicon_service.h" +#include "components/favicon/core/large_icon_service.h" #include "components/favicon_base/fallback_icon_style.h" +#include "components/favicon_base/favicon_types.h" #include "net/url_request/url_request.h" -#include "third_party/skia/include/core/SkColor.h" -#include "ui/gfx/color_analysis.h" -#include "ui/gfx/color_utils.h" namespace { -const int kDefaultLargeIconSize = 96; const int kMaxLargeIconSize = 192; // Arbitrary bound to safeguard endpoint. -const double kMaxBackgroundLuminance = 0.67; -const SkColor kDarkGray = SkColorSetRGB(0x78, 0x78, 0x78); -const SkColor kTextColor = SK_ColorWHITE; -const SkColor kDefaultBackgroundColor = kDarkGray; - } // namespace -LargeIconSource::IconRequest::IconRequest() : size(kDefaultLargeIconSize) { -} - -LargeIconSource::IconRequest::IconRequest( - const content::URLDataSource::GotDataCallback& callback_in, - const GURL& url_in, - int size_in) - : callback(callback_in), - url(url_in), - size(size_in) { -} - -LargeIconSource::IconRequest::~IconRequest() { -} - LargeIconSource::LargeIconSource( - favicon::FaviconService* favicon_service, - favicon::FallbackIconService* fallback_icon_service) - : favicon_service_(favicon_service), - fallback_icon_service_(fallback_icon_service) { - large_icon_types_.push_back(favicon_base::IconType::FAVICON); - large_icon_types_.push_back(favicon_base::IconType::TOUCH_ICON); - large_icon_types_.push_back(favicon_base::IconType::TOUCH_PRECOMPOSED_ICON); + favicon::FallbackIconService* fallback_icon_service, + favicon::LargeIconService* large_icon_service) + : fallback_icon_service_(fallback_icon_service), + large_icon_service_(large_icon_service) { } LargeIconSource::~LargeIconSource() { @@ -65,7 +41,7 @@ int render_process_id, int render_frame_id, const content::URLDataSource::GotDataCallback& callback) { - if (!favicon_service_) { + if (!large_icon_service_) { SendNotFoundResponse(callback); return; } @@ -85,14 +61,12 @@ return; } - favicon_service_->GetLargestRawFaviconForPageURL( + large_icon_service_->GetLargeIconOrFallbackStyle( url, - large_icon_types_, parser.size_in_pixels(), - base::Bind( - &LargeIconSource::OnIconDataAvailable, - base::Unretained(this), - IconRequest(callback, url, parser.size_in_pixels())), + base::Bind(&LargeIconSource::OnLargeIconDataAvailable, + base::Unretained(this), callback, url, + parser.size_in_pixels()), &cancelable_task_tracker_); } @@ -115,54 +89,25 @@ return URLDataSource::ShouldServiceRequest(request); } -void LargeIconSource::OnIconDataAvailable( - const IconRequest& request, - const favicon_base::FaviconRawBitmapResult& bitmap_result) { - if (!bitmap_result.is_valid()) { - SendDefaultFallbackIcon(request); +void LargeIconSource::OnLargeIconDataAvailable( + const content::URLDataSource::GotDataCallback& callback, + const GURL& url, + int size, + const favicon_base::LargeIconResult& result) { + if (result.bitmap.is_valid()) { + callback.Run(result.bitmap.bitmap_data.get()); return; } - // If we found a bitmap, but it's smaller than the requested size, we - // generate a fallback using the dominant color from the too-small bitmap. - // We adjust the luminance of the background so we can put light text over it. - if (bitmap_result.pixel_size.width() < request.size || - bitmap_result.pixel_size.height() < request.size) { - SkColor background = - color_utils::CalculateKMeanColorOfPNG(bitmap_result.bitmap_data); - color_utils::HSL background_hsl; - color_utils::SkColorToHSL(background, &background_hsl); - background_hsl.l = std::min(background_hsl.l, kMaxBackgroundLuminance); - background = color_utils::HSLToSkColor(background_hsl, SK_AlphaOPAQUE); - - // Now we can construct the fallback icon. - SendFallbackIcon(request, kTextColor, background); + // Bitmap is invalid, use the fallback if service is available. + if (!fallback_icon_service_ || !result.fallback_icon_style) { + SendNotFoundResponse(callback); return; } - - request.callback.Run(bitmap_result.bitmap_data.get()); -} - -void LargeIconSource::SendDefaultFallbackIcon(const IconRequest& request) { - SendFallbackIcon(request, kTextColor, kDefaultBackgroundColor); -} - -void LargeIconSource::SendFallbackIcon(const IconRequest& request, - SkColor text_color, - SkColor background_color) { - if (!fallback_icon_service_) { - SendNotFoundResponse(request.callback); - return; - } - favicon_base::FallbackIconStyle style; - style.background_color = background_color; - style.text_color = text_color; - style.font_size_ratio = 0.44; - style.roundness = 0; // Square. Round corners can be applied by JavaScript. std::vector<unsigned char> bitmap_data = fallback_icon_service_->RenderFallbackIconBitmap( - request.url, request.size, style); - request.callback.Run(base::RefCountedBytes::TakeVector(&bitmap_data)); + url, size, *result.fallback_icon_style); + callback.Run(base::RefCountedBytes::TakeVector(&bitmap_data)); } void LargeIconSource::SendNotFoundResponse(
diff --git a/chrome/browser/ui/webui/large_icon_source.h b/chrome/browser/ui/webui/large_icon_source.h index 05872d2..fb573ea 100644 --- a/chrome/browser/ui/webui/large_icon_source.h +++ b/chrome/browser/ui/webui/large_icon_source.h
@@ -6,18 +6,19 @@ #define CHROME_BROWSER_UI_WEBUI_LARGE_ICON_SOURCE_H_ #include <string> -#include <vector> #include "base/memory/scoped_ptr.h" #include "base/task/cancelable_task_tracker.h" #include "components/favicon/core/fallback_icon_service.h" -#include "components/favicon_base/favicon_types.h" #include "content/public/browser/url_data_source.h" -#include "third_party/skia/include/core/SkColor.h" namespace favicon { class FallbackIconService; -class FaviconService; +class LargeIconService; +} + +namespace favicon_base { +struct LargeIconResult; } // LargeIconSource services explicit chrome:// requests for large icons. @@ -35,10 +36,10 @@ // This requests a 48x48 large icon for http://www.google.com. class LargeIconSource : public content::URLDataSource { public: - // |favicon_service| and |fallback_icon_service| are owned by caller and may - // be null. - LargeIconSource(favicon::FaviconService* favicon_service, - favicon::FallbackIconService* fallback_icon_service); + // |fallback_icon_service| and |large_icon_service| are owned by caller and + // may be null. + LargeIconSource(favicon::FallbackIconService* fallback_icon_service, + favicon::LargeIconService* large_icon_service); ~LargeIconSource() override; @@ -53,34 +54,13 @@ bool ShouldReplaceExistingSource() const override; bool ShouldServiceRequest(const net::URLRequest* request) const override; - protected: - struct IconRequest { - IconRequest(); - IconRequest(const content::URLDataSource::GotDataCallback& callback_in, - const GURL& path_in, - int size_in); - ~IconRequest(); - - content::URLDataSource::GotDataCallback callback; - GURL url; - int size; - }; - private: - // Callback for icon data retrieval request. - void OnIconDataAvailable( - const IconRequest& request, - const favicon_base::FaviconRawBitmapResult& bitmap_result); - - // Renders and sends a default fallback icon. This is used when there is no - // known text and/or foreground color to use for the generated icon (it - // defaults to a light text color on a dark gray background). - void SendDefaultFallbackIcon(const IconRequest& request); - - // Renders and sends a fallback icon using the given colors. - void SendFallbackIcon(const IconRequest& request, - SkColor text_color, - SkColor background_color); + // Called with results of large icon retrieval request. + void OnLargeIconDataAvailable( + const content::URLDataSource::GotDataCallback& callback, + const GURL& url, + int size, + const favicon_base::LargeIconResult& bitmap_result); // Returns null to trigger "Not Found" response. void SendNotFoundResponse( @@ -88,15 +68,11 @@ base::CancelableTaskTracker cancelable_task_tracker_; - favicon::FaviconService* favicon_service_; - + // Owned by client. favicon::FallbackIconService* fallback_icon_service_; - // A pre-populated list of the types of icon files to consider when looking - // for the largest matching icon. - // Note: this is simply an optimization over populating an icon type vector - // on each request. - std::vector<int> large_icon_types_; + // Owned by client. + favicon::LargeIconService* large_icon_service_; DISALLOW_COPY_AND_ASSIGN(LargeIconSource); };
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 45d41c8..859b831 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
@@ -20,7 +20,6 @@ #include "chrome/common/url_constants.h" #include "chrome/test/base/ui_test_utils.h" #include "chrome/test/base/web_ui_browser_test.h" -#include "chromeos/chromeos_switches.h" #include "components/signin/core/browser/profile_oauth2_token_service.h" #include "components/signin/core/browser/signin_manager.h" #include "components/signin/core/browser/signin_manager_base.h" @@ -33,6 +32,7 @@ #if defined(OS_CHROMEOS) #include "base/prefs/pref_service.h" #include "chrome/common/pref_names.h" +#include "chromeos/chromeos_switches.h" #endif using testing::InvokeWithoutArgs;
diff --git a/chrome/browser/ui/webui/net_internals/net_internals_ui.cc b/chrome/browser/ui/webui/net_internals/net_internals_ui.cc index 78ca7bcc..c80cfbe 100644 --- a/chrome/browser/ui/webui/net_internals/net_internals_ui.cc +++ b/chrome/browser/ui/webui/net_internals/net_internals_ui.cc
@@ -299,7 +299,7 @@ #if defined(OS_WIN) void OnGetServiceProviders(const base::ListValue* list); #endif - void OnSetLogLevel(const base::ListValue* list); + void OnSetCaptureMode(const base::ListValue* list); // ChromeNetLog::ThreadSafeObserver implementation: void OnAddEntry(const net::NetLog::Entry& entry) override; @@ -466,9 +466,8 @@ #endif web_ui()->RegisterMessageCallback( - "setLogLevel", - base::Bind(&IOThreadImpl::CallbackHelper, - &IOThreadImpl::OnSetLogLevel, proxy_)); + "setCaptureMode", base::Bind(&IOThreadImpl::CallbackHelper, + &IOThreadImpl::OnSetCaptureMode, proxy_)); web_ui()->RegisterMessageCallback( "clearBrowserCache", base::Bind(&NetInternalsMessageHandler::OnClearBrowserCache, @@ -683,8 +682,8 @@ PrePopulateEventList(); // Register with network stack to observe events. - io_thread_->net_log()->DeprecatedAddObserver(this, - net::NetLog::LOG_ALL_BUT_BYTES); + io_thread_->net_log()->DeprecatedAddObserver( + this, net::NetLogCaptureMode::IncludeCookiesAndCredentials()); } void NetInternalsMessageHandler::IOThreadImpl::OnGetNetInfo( @@ -1070,20 +1069,25 @@ } #endif // defined(OS_CHROMEOS) -void NetInternalsMessageHandler::IOThreadImpl::OnSetLogLevel( +void NetInternalsMessageHandler::IOThreadImpl::OnSetCaptureMode( const base::ListValue* list) { - int log_level; - std::string log_level_string; - if (!list->GetString(0, &log_level_string) || - !base::StringToInt(log_level_string, &log_level)) { + std::string capture_mode_string; + if (!list->GetString(0, &capture_mode_string)) { NOTREACHED(); return; } - DCHECK_GE(log_level, net::NetLog::LOG_ALL); - DCHECK_LT(log_level, net::NetLog::LOG_NONE); - net_log()->SetObserverLogLevel( - this, static_cast<net::NetLog::LogLevel>(log_level)); + // Convert the string to a NetLogCaptureMode. + net::NetLogCaptureMode mode; + if (capture_mode_string == "IncludeSocketBytes") { + mode = net::NetLogCaptureMode::IncludeSocketBytes(); + } else if (capture_mode_string == "IncludeCookiesAndCredentials") { + mode = net::NetLogCaptureMode::IncludeCookiesAndCredentials(); + } else { + NOTREACHED(); + } + + net_log()->SetObserverCaptureMode(this, mode); } // Note that unlike other methods of IOThreadImpl, this function
diff --git a/chrome/browser/ui/webui/ntp/app_launcher_handler.cc b/chrome/browser/ui/webui/ntp/app_launcher_handler.cc index 801ba18..69899bc 100644 --- a/chrome/browser/ui/webui/ntp/app_launcher_handler.cc +++ b/chrome/browser/ui/webui/ntp/app_launcher_handler.cc
@@ -71,7 +71,6 @@ using extensions::ExtensionPrefs; using extensions::ExtensionRegistry; using extensions::ExtensionSet; -using extensions::UnloadedExtensionInfo; namespace { @@ -104,7 +103,9 @@ RecordAppLauncherPromoHistogram(apps::APP_LAUNCHER_PROMO_SHOWN); } -AppLauncherHandler::~AppLauncherHandler() {} +AppLauncherHandler::~AppLauncherHandler() { + ExtensionRegistry::Get(Profile::FromWebUI(web_ui()))->RemoveObserver(this); +} void AppLauncherHandler::CreateAppInfo( const Extension* extension, @@ -261,71 +262,6 @@ return; switch (type) { - case extensions::NOTIFICATION_EXTENSION_LOADED_DEPRECATED: { - const Extension* extension = - content::Details<const Extension>(details).ptr(); - if (!extension->is_app()) - return; - - if (!extensions::ui_util::ShouldDisplayInNewTabPage( - extension, Profile::FromWebUI(web_ui()))) { - return; - } - - scoped_ptr<base::DictionaryValue> app_info(GetAppInfo(extension)); - if (app_info.get()) { - visible_apps_.insert(extension->id()); - - ExtensionPrefs* prefs = - ExtensionPrefs::Get(extension_service_->profile()); - base::FundamentalValue highlight( - prefs->IsFromBookmark(extension->id()) && - attempted_bookmark_app_install_); - attempted_bookmark_app_install_ = false; - web_ui()->CallJavascriptFunction("ntp.appAdded", *app_info, highlight); - } - - break; - } - case extensions::NOTIFICATION_EXTENSION_UNLOADED_DEPRECATED: - case extensions::NOTIFICATION_EXTENSION_UNINSTALLED_DEPRECATED: { - const Extension* extension = NULL; - bool uninstalled = false; - if (type == extensions::NOTIFICATION_EXTENSION_UNINSTALLED_DEPRECATED) { - extension = content::Details<const Extension>(details).ptr(); - uninstalled = true; - } else { // NOTIFICATION_EXTENSION_UNLOADED_DEPRECATED - if (content::Details<UnloadedExtensionInfo>(details)->reason == - UnloadedExtensionInfo::REASON_UNINSTALL) { - // Uninstalls are tracked by - // NOTIFICATION_EXTENSION_UNINSTALLED_DEPRECATED. - return; - } - extension = content::Details<extensions::UnloadedExtensionInfo>( - details)->extension; - uninstalled = false; - } - if (!extension->is_app()) - return; - - if (!extensions::ui_util::ShouldDisplayInNewTabPage( - extension, Profile::FromWebUI(web_ui()))) { - return; - } - - scoped_ptr<base::DictionaryValue> app_info(GetAppInfo(extension)); - if (app_info.get()) { - if (uninstalled) - visible_apps_.erase(extension->id()); - - web_ui()->CallJavascriptFunction( - "ntp.appRemoved", - *app_info, - base::FundamentalValue(uninstalled), - base::FundamentalValue(!extension_id_prompting_.empty())); - } - break; - } case chrome::NOTIFICATION_APP_LAUNCHER_REORDERED: { const std::string* id = content::Details<const std::string>(details).ptr(); @@ -364,6 +300,38 @@ } } +void AppLauncherHandler::OnExtensionLoaded( + content::BrowserContext* browser_context, + const Extension* extension) { + if (!ShouldShow(extension)) + return; + + scoped_ptr<base::DictionaryValue> app_info(GetAppInfo(extension)); + if (!app_info.get()) + return; + + visible_apps_.insert(extension->id()); + ExtensionPrefs* prefs = ExtensionPrefs::Get(extension_service_->profile()); + base::FundamentalValue highlight(prefs->IsFromBookmark(extension->id()) && + attempted_bookmark_app_install_); + attempted_bookmark_app_install_ = false; + web_ui()->CallJavascriptFunction("ntp.appAdded", *app_info, highlight); +} + +void AppLauncherHandler::OnExtensionUnloaded( + content::BrowserContext* browser_context, + const Extension* extension, + extensions::UnloadedExtensionInfo::Reason reason) { + AppRemoved(extension, false); +} + +void AppLauncherHandler::OnExtensionUninstalled( + content::BrowserContext* browser_context, + const Extension* extension, + extensions::UninstallReason reason) { + AppRemoved(extension, true); +} + void AppLauncherHandler::FillAppDictionary(base::DictionaryValue* dictionary) { // CreateAppInfo and ClearOrdinals can change the extension prefs. base::AutoReset<bool> auto_reset(&ignore_changes_, true); @@ -462,15 +430,7 @@ extensions::pref_names::kExtensions, callback); extension_pref_change_registrar_.Add(prefs::kNtpAppPageNames, callback); - registrar_.Add(this, - extensions::NOTIFICATION_EXTENSION_LOADED_DEPRECATED, - content::Source<Profile>(profile)); - registrar_.Add(this, - extensions::NOTIFICATION_EXTENSION_UNLOADED_DEPRECATED, - content::Source<Profile>(profile)); - registrar_.Add(this, - extensions::NOTIFICATION_EXTENSION_UNINSTALLED_DEPRECATED, - content::Source<Profile>(profile)); + ExtensionRegistry::Get(profile)->AddObserver(this); registrar_.Add(this, chrome::NOTIFICATION_APP_LAUNCHER_REORDERED, content::Source<AppSorting>( @@ -875,3 +835,25 @@ } return extension_uninstall_dialog_.get(); } + +void AppLauncherHandler::AppRemoved(const Extension* extension, + bool is_uninstall) { + if (!ShouldShow(extension)) + return; + + scoped_ptr<base::DictionaryValue> app_info(GetAppInfo(extension)); + if (!app_info.get()) + return; + + web_ui()->CallJavascriptFunction( + "ntp.appRemoved", *app_info, base::FundamentalValue(is_uninstall), + base::FundamentalValue(!extension_id_prompting_.empty())); +} + +bool AppLauncherHandler::ShouldShow(const Extension* extension) const { + if (ignore_changes_ || !has_loaded_apps_ || !extension->is_app()) + return false; + + Profile* profile = Profile::FromWebUI(web_ui()); + return extensions::ui_util::ShouldDisplayInNewTabPage(extension, profile); +}
diff --git a/chrome/browser/ui/webui/ntp/app_launcher_handler.h b/chrome/browser/ui/webui/ntp/app_launcher_handler.h index 0e31f88..85c451e 100644 --- a/chrome/browser/ui/webui/ntp/app_launcher_handler.h +++ b/chrome/browser/ui/webui/ntp/app_launcher_handler.h
@@ -19,6 +19,7 @@ #include "content/public/browser/notification_observer.h" #include "content/public/browser/notification_registrar.h" #include "content/public/browser/web_ui_message_handler.h" +#include "extensions/browser/extension_registry_observer.h" #include "extensions/common/extension.h" #include "sync/api/string_ordinal.h" @@ -36,7 +37,8 @@ : public content::WebUIMessageHandler, public extensions::ExtensionUninstallDialog::Delegate, public ExtensionEnableFlowDelegate, - public content::NotificationObserver { + public content::NotificationObserver, + public extensions::ExtensionRegistryObserver { public: explicit AppLauncherHandler(ExtensionService* extension_service); ~AppLauncherHandler() override; @@ -47,14 +49,25 @@ ExtensionService* service, base::DictionaryValue* value); - // WebUIMessageHandler implementation. + // WebUIMessageHandler: void RegisterMessages() override; - // content::NotificationObserver + // content::NotificationObserver: void Observe(int type, const content::NotificationSource& source, const content::NotificationDetails& details) override; + // extensions::ExtensionRegistryObserver: + void OnExtensionLoaded(content::BrowserContext* browser_context, + const extensions::Extension* extension) override; + void OnExtensionUnloaded( + content::BrowserContext* browser_context, + const extensions::Extension* extension, + extensions::UnloadedExtensionInfo::Reason reason) override; + void OnExtensionUninstalled(content::BrowserContext* browser_context, + const extensions::Extension* extension, + extensions::UninstallReason reason) override; + // Populate the given dictionary with all installed app info. void FillAppDictionary(base::DictionaryValue* value); @@ -149,6 +162,12 @@ void OnLocalStatePreferenceChanged(); + // Called when an app is removed (unloaded or uninstalled). Updates the UI. + void AppRemoved(const extensions::Extension* extension, bool is_uninstall); + + // True if the extension should be displayed. + bool ShouldShow(const extensions::Extension* extension) const; + // The apps are represented in the extensions model, which // outlives us since it's owned by our containing profile. ExtensionService* const extension_service_;
diff --git a/chrome/browser/ui/webui/ntp/most_visited_handler.cc b/chrome/browser/ui/webui/ntp/most_visited_handler.cc index cff19a6..e8e8a3f 100644 --- a/chrome/browser/ui/webui/ntp/most_visited_handler.cc +++ b/chrome/browser/ui/webui/ntp/most_visited_handler.cc
@@ -21,7 +21,7 @@ #include "base/threading/thread.h" #include "base/values.h" #include "chrome/browser/favicon/fallback_icon_service_factory.h" -#include "chrome/browser/favicon/favicon_service_factory.h" +#include "chrome/browser/favicon/large_icon_service_factory.h" #include "chrome/browser/history/top_sites_factory.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/thumbnails/thumbnail_list_source.h" @@ -38,7 +38,7 @@ #include "chrome/common/pref_names.h" #include "chrome/common/url_constants.h" #include "components/favicon/core/fallback_icon_service.h" -#include "components/favicon/core/favicon_service.h" +#include "components/favicon/core/large_icon_service.h" #include "components/history/core/browser/page_usage_data.h" #include "components/history/core/browser/top_sites.h" #include "components/keyed_service/core/service_access_type.h" @@ -86,15 +86,12 @@ // Set up our sources for top-sites data. content::URLDataSource::Add(profile, new ThumbnailListSource(profile)); - favicon::FaviconService* favicon_service = - FaviconServiceFactory::GetForProfile(profile, - ServiceAccessType::EXPLICIT_ACCESS); favicon::FallbackIconService* fallback_icon_service = FallbackIconServiceFactory::GetForBrowserContext(profile); + favicon::LargeIconService* large_icon_service = + LargeIconServiceFactory::GetForBrowserContext(profile); - // Register chrome://large-icon as a data source for large icons. - content::URLDataSource::Add(profile, - new LargeIconSource(favicon_service, fallback_icon_service)); + // Register chrome://fallback-icon as a data source for fallback icons. content::URLDataSource::Add(profile, new FallbackIconSource(fallback_icon_service)); @@ -102,6 +99,10 @@ content::URLDataSource::Add( profile, new FaviconSource(profile, FaviconSource::FAVICON)); + // Register chrome://large-icon as a data source for large icons. + content::URLDataSource::Add( + profile, new LargeIconSource(fallback_icon_service, large_icon_service)); + scoped_refptr<history::TopSites> top_sites = TopSitesFactory::GetForProfile(profile); if (top_sites) {
diff --git a/chrome/browser/ui/webui/options/browser_options_handler.cc b/chrome/browser/ui/webui/options/browser_options_handler.cc index f19127f..d1f6f6f0 100644 --- a/chrome/browser/ui/webui/options/browser_options_handler.cc +++ b/chrome/browser/ui/webui/options/browser_options_handler.cc
@@ -69,7 +69,6 @@ #include "chrome/grit/chromium_strings.h" #include "chrome/grit/generated_resources.h" #include "chrome/grit/locale_settings.h" -#include "chromeos/chromeos_switches.h" #include "components/policy/core/common/policy_map.h" #include "components/policy/core/common/policy_namespace.h" #include "components/policy/core/common/policy_service.h" @@ -120,6 +119,7 @@ #include "chrome/browser/policy/profile_policy_connector.h" #include "chrome/browser/policy/profile_policy_connector_factory.h" #include "chrome/browser/ui/browser_window.h" +#include "chromeos/chromeos_switches.h" #include "chromeos/dbus/dbus_thread_manager.h" #include "chromeos/dbus/power_manager_client.h" #include "components/user_manager/user.h"
diff --git a/chrome/browser/ui/webui/options/chromeos/change_picture_options_handler.cc b/chrome/browser/ui/webui/options/chromeos/change_picture_options_handler.cc index 2af44e0f..b6de55d1 100644 --- a/chrome/browser/ui/webui/options/chromeos/change_picture_options_handler.cc +++ b/chrome/browser/ui/webui/options/chromeos/change_picture_options_handler.cc
@@ -77,9 +77,7 @@ } // namespace ChangePictureOptionsHandler::ChangePictureOptionsHandler() - : ImageRequest( - BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI)), - previous_image_url_(url::kAboutBlankURL), + : previous_image_url_(url::kAboutBlankURL), previous_image_index_(user_manager::User::USER_IMAGE_INVALID) { registrar_.Add(this, chrome::NOTIFICATION_PROFILE_IMAGE_UPDATED, content::NotificationService::AllSources());
diff --git a/chrome/browser/ui/webui/options/chromeos/display_options_handler.cc b/chrome/browser/ui/webui/options/chromeos/display_options_handler.cc index d354743..ca4cc50 100644 --- a/chrome/browser/ui/webui/options/chromeos/display_options_handler.cc +++ b/chrome/browser/ui/webui/options/chromeos/display_options_handler.cc
@@ -124,7 +124,7 @@ base::DictionaryValue* ConvertDisplayModeToValue(int64 display_id, const ash::DisplayMode& mode) { - bool is_internal = GetDisplayManager()->IsInternalDisplayId(display_id); + bool is_internal = gfx::Display::InternalDisplayId() == display_id; base::DictionaryValue* result = new base::DictionaryValue(); gfx::Size size_dip = mode.GetSizeInDIP(is_internal); result->SetInteger("width", size_dip.width()); @@ -260,7 +260,7 @@ void DisplayOptionsHandler::SendDisplayInfo( const std::vector<gfx::Display>& displays) { DisplayManager* display_manager = GetDisplayManager(); - base::FundamentalValue mirroring(display_manager->IsMirrored()); + base::FundamentalValue mirroring(display_manager->IsInMirrorMode()); int64 primary_id = ash::Shell::GetScreen()->GetPrimaryDisplay().id(); base::ListValue js_displays;
diff --git a/chrome/browser/ui/webui/options/chromeos/internet_options_handler.cc b/chrome/browser/ui/webui/options/chromeos/internet_options_handler.cc index 774433f..036770a 100644 --- a/chrome/browser/ui/webui/options/chromeos/internet_options_handler.cc +++ b/chrome/browser/ui/webui/options/chromeos/internet_options_handler.cc
@@ -13,16 +13,10 @@ #include "base/basictypes.h" #include "base/bind.h" #include "base/bind_helpers.h" -#include "base/command_line.h" #include "base/memory/scoped_ptr.h" -#include "base/strings/string_util.h" -#include "base/strings/utf_string_conversions.h" #include "base/values.h" -#include "chrome/browser/browser_process.h" #include "chrome/browser/chromeos/mobile_config.h" -#include "chrome/browser/chromeos/net/onc_utils.h" #include "chrome/browser/chromeos/options/network_config_view.h" -#include "chrome/browser/chromeos/options/network_property_ui_data.h" #include "chrome/browser/chromeos/profiles/profile_helper.h" #include "chrome/browser/chromeos/settings/cros_settings.h" #include "chrome/browser/chromeos/sim_dialog_delegate.h" @@ -32,23 +26,9 @@ #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/webui/chromeos/mobile_setup_dialog.h" #include "chrome/browser/ui/webui/options/chromeos/internet_options_handler_strings.h" -#include "chromeos/chromeos_switches.h" #include "chromeos/login/login_state.h" -#include "chromeos/network/device_state.h" -#include "chromeos/network/managed_network_configuration_handler.h" -#include "chromeos/network/network_connection_handler.h" -#include "chromeos/network/network_device_handler.h" -#include "chromeos/network/network_event_log.h" -#include "chromeos/network/network_ip_config.h" -#include "chromeos/network/network_profile.h" -#include "chromeos/network/network_profile_handler.h" #include "chromeos/network/network_state.h" #include "chromeos/network/network_state_handler.h" -#include "chromeos/network/network_util.h" -#include "chromeos/network/onc/onc_signature.h" -#include "chromeos/network/onc/onc_translation_tables.h" -#include "chromeos/network/onc/onc_translator.h" -#include "chromeos/network/onc/onc_utils.h" #include "components/onc/onc_constants.h" #include "components/user_manager/user_manager.h" #include "content/public/browser/web_contents.h" @@ -74,26 +54,14 @@ // Keys for the initial "localized" dictionary values. const char kLoggedInAsOwnerKey[] = "loggedInAsOwner"; const char kShowCarrierSelectKey[] = "showCarrierSelect"; -const char kNetworkDataKey[] = "networkData"; - -// Keys for the network description dictionary passed to the web ui. Make sure -// to keep the strings in sync with what the JavaScript side uses. -const char kNetworkInfoKeyPolicyManaged[] = "policyManaged"; // Functions we call in JavaScript. const char kSetVPNProvidersFunction[] = "options.VPNProviders.setProviders"; -const char kRefreshNetworkDataFunction[] = - "options.network.NetworkList.refreshNetworkData"; -const char kUpdateConnectionDataFunction[] = - "options.internet.DetailsInternetPage.updateConnectionData"; // JS methods to show additional UI. const char kShowMorePlanInfoMessage[] = "showMorePlanInfo"; const char kSimOperationMessage[] = "simOperation"; -// TODO(stevenjb): Deprecate this once we handle events in the JS. -const char kSetNetworkGuidMessage[] = "setNetworkGuid"; - // TODO(stevenjb): Deprecate these and integrate with settings Web UI. const char kAddVPNConnectionMessage[] = "addVPNConnection"; const char kAddNonVPNConnectionMessage[] = "addNonVPNConnection"; @@ -102,10 +70,6 @@ const char kLoadVPNProviders[] = "loadVPNProviders"; // These are strings used to communicate with JavaScript. -const char kTagCellularSimAbsent[] = "cellularSimAbsent"; -const char kTagCellularSimLockType[] = "cellularSimLockType"; -const char kTagCellularSupportsScan[] = "cellularSupportsScan"; -const char kTagRememberedList[] = "rememberedList"; const char kTagSimOpChangePin[] = "changePin"; const char kTagSimOpConfigure[] = "configure"; const char kTagSimOpSetLocked[] = "setLocked"; @@ -113,21 +77,6 @@ const char kTagSimOpUnlock[] = "unlock"; const char kTagVPNProviderName[] = "name"; const char kTagVPNProviderExtensionID[] = "extensionID"; -const char kTagVpnList[] = "vpnList"; -const char kTagWiredList[] = "wiredList"; -const char kTagWirelessList[] = "wirelessList"; - -void ShillError(const std::string& function, - const std::string& error_name, - scoped_ptr<base::DictionaryValue> error_data) { - // UpdateConnectionData may send requests for stale services; ignore - // these errors. - if (function == "UpdateConnectionData" && - error_name == network_handler::kDBusFailedError) - return; - NET_LOG_ERROR("Shill Error from InternetOptionsHandler: " + error_name, - function); -} const NetworkState* GetNetworkState(const std::string& service_path) { return NetworkHandler::Get()->network_state_handler()-> @@ -141,22 +90,6 @@ return network ? network->path() : ""; } -// Builds a dictionary with network information for the NetworkList on the -// settings page. Ownership of the returned pointer is transferred to the -// caller. TODO(stevenjb): Replace with calls to networkingPrivate.getNetworks. -base::DictionaryValue* BuildNetworkDictionary( - const NetworkState* network, - const PrefService* profile_prefs) { - scoped_ptr<base::DictionaryValue> network_info = - network_util::TranslateNetworkStateToONC(network); - - bool has_policy = onc::HasPolicyForNetwork( - profile_prefs, g_browser_process->local_state(), *network); - network_info->SetBoolean(kNetworkInfoKeyPolicyManaged, has_policy); - - return network_info.release(); -} - bool IsVPNProvider(const extensions::Extension* extension) { return extension->permissions_data()->HasAPIPermission( extensions::APIPermission::kVpnProvider); @@ -188,14 +121,9 @@ InternetOptionsHandler::InternetOptionsHandler() : weak_factory_(this) { GetExtensionRegistryForPrimaryUser()->AddObserver(this); - NetworkHandler::Get()->network_state_handler()->AddObserver(this, FROM_HERE); } InternetOptionsHandler::~InternetOptionsHandler() { - if (NetworkHandler::IsInitialized()) { - NetworkHandler::Get()->network_state_handler()->RemoveObserver( - this, FROM_HERE); - } GetExtensionRegistryForPrimaryUser()->RemoveObserver(this); } @@ -213,18 +141,11 @@ localized_strings->SetBoolean(kLoggedInAsOwnerKey, logged_in_as_owner); // TODO(anujsharma): Remove kShowCarrierSelectKey, as it is not // required anymore. - localized_strings->SetBoolean( - kShowCarrierSelectKey, false); - - base::DictionaryValue* network_dictionary = new base::DictionaryValue; - FillNetworkInfo(network_dictionary); - localized_strings->Set(kNetworkDataKey, network_dictionary); + localized_strings->SetBoolean(kShowCarrierSelectKey, false); } void InternetOptionsHandler::InitializePage() { UpdateVPNProviders(); - NetworkHandler::Get()->network_state_handler()->RequestScan(); - RefreshNetworkData(); } void InternetOptionsHandler::RegisterMessages() { @@ -247,10 +168,6 @@ kLoadVPNProviders, base::Bind(&InternetOptionsHandler::LoadVPNProvidersCallback, base::Unretained(this))); - - web_ui()->RegisterMessageCallback(kSetNetworkGuidMessage, - base::Bind(&InternetOptionsHandler::SetNetworkGuidCallback, - base::Unretained(this))); } void InternetOptionsHandler::OnExtensionLoaded( @@ -320,19 +237,6 @@ } //////////////////////////////////////////////////////////////////////////////// -// TODO(stevenjb): Deprecate this once events are handled in the JS. - -void InternetOptionsHandler::SetNetworkGuidCallback( - const base::ListValue* args) { - std::string guid; - if (args->GetSize() != 1 || !args->GetString(0, &guid)) { - NOTREACHED(); - return; - } - details_guid_ = guid; -} - -//////////////////////////////////////////////////////////////////////////////// void InternetOptionsHandler::UpdateVPNProviders() { extensions::ExtensionRegistry* const registry = @@ -354,73 +258,6 @@ web_ui()->CallJavascriptFunction(kSetVPNProvidersFunction, vpn_providers); } -void InternetOptionsHandler::RefreshNetworkData() { - base::DictionaryValue dictionary; - FillNetworkInfo(&dictionary); - web_ui()->CallJavascriptFunction(kRefreshNetworkDataFunction, dictionary); -} - -void InternetOptionsHandler::UpdateConnectionData( - const std::string& service_path) { - NetworkHandler::Get() - ->managed_network_configuration_handler() - ->GetManagedProperties( - LoginState::Get()->primary_user_hash(), service_path, - base::Bind(&InternetOptionsHandler::GetManagedPropertiesResult, - weak_factory_.GetWeakPtr(), kUpdateConnectionDataFunction), - base::Bind(&ShillError, "UpdateConnectionData")); -} - -void InternetOptionsHandler::GetManagedPropertiesResult( - const std::string& js_callback_function, - const std::string& service_path, - const base::DictionaryValue& onc_properties) { - scoped_ptr<base::DictionaryValue> dictionary(onc_properties.DeepCopy()); - web_ui()->CallJavascriptFunction(js_callback_function, *dictionary); -} - -void InternetOptionsHandler::DeviceListChanged() { - if (!web_ui()) - return; - RefreshNetworkData(); -} - -void InternetOptionsHandler::NetworkListChanged() { - if (!web_ui()) - return; - RefreshNetworkData(); -} - -void InternetOptionsHandler::NetworkConnectionStateChanged( - const NetworkState* network) { - if (!web_ui()) - return; - if (network->guid() == details_guid_) - UpdateConnectionData(network->path()); -} - -void InternetOptionsHandler::NetworkPropertiesUpdated( - const NetworkState* network) { - if (!web_ui()) - return; - RefreshNetworkData(); - if (network->guid() == details_guid_) - UpdateConnectionData(network->path()); -} - -void InternetOptionsHandler::DevicePropertiesUpdated( - const DeviceState* device) { - if (!web_ui()) - return; - if (device->type() != shill::kTypeCellular) - return; - const NetworkState* network = - NetworkHandler::Get()->network_state_handler()->FirstNetworkByType( - NetworkTypePattern::Cellular()); - if (network && network->path() == details_guid_) - UpdateConnectionData(network->path()); -} - gfx::NativeWindow InternetOptionsHandler::GetNativeWindow() const { return web_ui()->GetWebContents()->GetTopLevelNativeWindow(); } @@ -496,84 +333,5 @@ UpdateVPNProviders(); } -base::ListValue* InternetOptionsHandler::GetWiredList() { - base::ListValue* list = new base::ListValue(); - const NetworkState* network = NetworkHandler::Get()->network_state_handler()-> - FirstNetworkByType(NetworkTypePattern::Ethernet()); - if (!network) - return list; - list->Append(BuildNetworkDictionary(network, GetPrefs())); - return list; -} - -base::ListValue* InternetOptionsHandler::GetWirelessList() { - base::ListValue* list = new base::ListValue(); - - NetworkStateHandler::NetworkStateList networks; - NetworkHandler::Get()->network_state_handler()->GetVisibleNetworkListByType( - NetworkTypePattern::Wireless(), &networks); - for (NetworkStateHandler::NetworkStateList::const_iterator iter = - networks.begin(); iter != networks.end(); ++iter) { - list->Append(BuildNetworkDictionary(*iter, GetPrefs())); - } - - return list; -} - -base::ListValue* InternetOptionsHandler::GetVPNList() { - base::ListValue* list = new base::ListValue(); - - NetworkStateHandler::NetworkStateList networks; - NetworkHandler::Get()->network_state_handler()->GetVisibleNetworkListByType( - NetworkTypePattern::VPN(), &networks); - for (NetworkStateHandler::NetworkStateList::const_iterator iter = - networks.begin(); iter != networks.end(); ++iter) { - list->Append(BuildNetworkDictionary(*iter, GetPrefs())); - } - - return list; -} - -base::ListValue* InternetOptionsHandler::GetRememberedList() { - base::ListValue* list = new base::ListValue(); - - NetworkStateHandler::NetworkStateList networks; - NetworkHandler::Get()->network_state_handler()->GetNetworkListByType( - NetworkTypePattern::Default(), - true /* configured_only */, - false /* visible_only */, - 0 /* no limit */, - &networks); - for (NetworkStateHandler::NetworkStateList::const_iterator iter = - networks.begin(); iter != networks.end(); ++iter) { - const NetworkState* network = *iter; - if (network->type() != shill::kTypeWifi && - network->type() != shill::kTypeVPN) { - continue; - } - list->Append(BuildNetworkDictionary(network, GetPrefs())); - } - - return list; -} - -void InternetOptionsHandler::FillNetworkInfo( - base::DictionaryValue* dictionary) { - NetworkStateHandler* handler = NetworkHandler::Get()->network_state_handler(); - dictionary->Set(kTagWiredList, GetWiredList()); - dictionary->Set(kTagWirelessList, GetWirelessList()); - dictionary->Set(kTagVpnList, GetVPNList()); - dictionary->Set(kTagRememberedList, GetRememberedList()); - - const DeviceState* cellular = - handler->GetDeviceStateByType(NetworkTypePattern::Mobile()); - dictionary->SetBoolean(kTagCellularSupportsScan, - cellular && cellular->support_network_scan()); - dictionary->SetBoolean(kTagCellularSimAbsent, - cellular && cellular->IsSimAbsent()); - dictionary->SetString(kTagCellularSimLockType, - cellular ? cellular->sim_lock_type() : ""); -} - } // namespace options } // namespace chromeos
diff --git a/chrome/browser/ui/webui/options/chromeos/internet_options_handler.h b/chrome/browser/ui/webui/options/chromeos/internet_options_handler.h index c7e91887..17c3030 100644 --- a/chrome/browser/ui/webui/options/chromeos/internet_options_handler.h +++ b/chrome/browser/ui/webui/options/chromeos/internet_options_handler.h
@@ -10,19 +10,12 @@ #include "base/compiler_specific.h" #include "base/memory/weak_ptr.h" #include "chrome/browser/ui/webui/options/options_ui.h" -#include "chromeos/network/network_state_handler_observer.h" #include "extensions/browser/extension_registry_observer.h" #include "ui/gfx/native_widget_types.h" class Browser; class PrefService; -namespace chromeos { -class DeviceState; -class NetworkState; -class NetworkStateHandlerObserver; -} - namespace gfx { class ImageSkia; } @@ -36,7 +29,6 @@ // ChromeOS internet options page UI handler. class InternetOptionsHandler : public ::options::OptionsPageUIHandler, - public chromeos::NetworkStateHandlerObserver, public extensions::ExtensionRegistryObserver { public: InternetOptionsHandler(); @@ -63,39 +55,9 @@ void ShowMorePlanInfoCallback(const base::ListValue* args); void SimOperationCallback(const base::ListValue* args); - // Sets details_guid_ for event forwarding. - void SetNetworkGuidCallback(const base::ListValue* args); - - // networkingPrvate callbacks - void GetManagedPropertiesCallback(const base::ListValue* args); - // Updates the list of VPN providers enabled in the primary user's profile. void UpdateVPNProviders(); - // Refreshes the display of network information. - void RefreshNetworkData(); - - // Updates the display of network connection information for the details page - // if visible. - void UpdateConnectionData(const std::string& service_path); - - // Callback for ManagedNetworkConnectionHandler::GetManagedProperties. - // Calls the JS callback |js_callback_function| with the result. - void GetManagedPropertiesResult(const std::string& js_callback_function, - const std::string& service_path, - const base::DictionaryValue& onc_properties); - - // NetworkStateHandlerObserver - void DeviceListChanged() override; - void NetworkListChanged() override; - void NetworkConnectionStateChanged( - const chromeos::NetworkState* network) override; - void NetworkPropertiesUpdated(const chromeos::NetworkState* network) override; - void DevicePropertiesUpdated(const chromeos::DeviceState* device) override; - - // Updates the logged in user type. - void UpdateLoggedInUserType(); - // Gets the native window for hosting dialogs, etc. gfx::NativeWindow GetNativeWindow() const; @@ -111,24 +73,6 @@ // profile be sent to JavaScript. void LoadVPNProvidersCallback(const base::ListValue* args); - // Creates the map of wired networks. - base::ListValue* GetWiredList(); - - // Creates the map of wireless networks. - base::ListValue* GetWirelessList(); - - // Creates the map of virtual networks. - base::ListValue* GetVPNList(); - - // Creates the map of remembered networks. - base::ListValue* GetRememberedList(); - - // Fills network information into JS dictionary for displaying network lists. - void FillNetworkInfo(base::DictionaryValue* dictionary); - - // Keep track of the service path for the network shown in the Details view. - std::string details_guid_; - // Weak pointer factory so we can start connections at a later time // without worrying that they will actually try to happen after the lifetime // of this object.
diff --git a/chrome/browser/ui/webui/signin/profile_signin_confirmation_dialog.cc b/chrome/browser/ui/webui/signin/profile_signin_confirmation_dialog.cc index f431323..1c688bb 100644 --- a/chrome/browser/ui/webui/signin/profile_signin_confirmation_dialog.cc +++ b/chrome/browser/ui/webui/signin/profile_signin_confirmation_dialog.cc
@@ -186,8 +186,7 @@ void ProfileSigninConfirmationDialog::OnCloseContents( content::WebContents* source, bool* out_close_dialog) { - if (out_close_dialog) - *out_close_dialog = true; + *out_close_dialog = true; } bool ProfileSigninConfirmationDialog::ShouldShowDialogTitle() const {
diff --git a/chrome/browser/web_applications/web_app.cc b/chrome/browser/web_applications/web_app.cc index 627d78f..55b69307 100644 --- a/chrome/browser/web_applications/web_app.cc +++ b/chrome/browser/web_applications/web_app.cc
@@ -383,7 +383,7 @@ const ShortcutLocations& locations, scoped_ptr<ShortcutInfo> shortcut_info, const extensions::FileHandlersInfo& file_handlers_info) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); // If the shortcut is for an application shortcut with the new bookmark app // flow disabled, there will be no corresponding extension. @@ -419,7 +419,7 @@ const ShortcutLocations& locations, Profile* profile, const extensions::Extension* app) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); if (!ShouldCreateShortcutFor(reason, profile, app)) return; @@ -429,7 +429,7 @@ } void DeleteAllShortcuts(Profile* profile, const extensions::Extension* app) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); scoped_ptr<ShortcutInfo> shortcut_info( ShortcutInfoForExtensionAndProfile(app, profile)); @@ -443,7 +443,7 @@ void UpdateAllShortcuts(const base::string16& old_app_title, Profile* profile, const extensions::Extension* app) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); GetInfoForApp(app, profile,
diff --git a/chrome/browser/web_applications/web_app_linux.cc b/chrome/browser/web_applications/web_app_linux.cc index e6636dd..193375c 100644 --- a/chrome/browser/web_applications/web_app_linux.cc +++ b/chrome/browser/web_applications/web_app_linux.cc
@@ -25,7 +25,7 @@ const ShortcutLocations& creation_locations, ShortcutCreationReason /*creation_reason*/) { #if !defined(OS_CHROMEOS) - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE)); + DCHECK_CURRENTLY_ON(content::BrowserThread::FILE); return shell_integration_linux::CreateDesktopShortcut(*shortcut_info, creation_locations); #else @@ -46,7 +46,7 @@ const base::string16& /*old_app_title*/, scoped_ptr<ShortcutInfo> shortcut_info, const extensions::FileHandlersInfo& file_handlers_info) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE)); + DCHECK_CURRENTLY_ON(content::BrowserThread::FILE); scoped_ptr<base::Environment> env(base::Environment::Create());
diff --git a/chrome/browser/web_applications/web_app_mac.mm b/chrome/browser/web_applications/web_app_mac.mm index 6c80590..aaa8446 100644 --- a/chrome/browser/web_applications/web_app_mac.mm +++ b/chrome/browser/web_applications/web_app_mac.mm
@@ -221,7 +221,7 @@ void LaunchShimOnFileThread(scoped_ptr<web_app::ShortcutInfo> shortcut_info, bool launched_after_rebuild) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE)); + DCHECK_CURRENTLY_ON(content::BrowserThread::FILE); base::FilePath shim_path = web_app::GetAppInstallPath(*shortcut_info); if (shim_path.empty() || @@ -258,7 +258,7 @@ const base::string16& old_app_title, const web_app::ShortcutInfo& shortcut_info, const extensions::FileHandlersInfo& file_handlers_info) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE)); + DCHECK_CURRENTLY_ON(content::BrowserThread::FILE); if (AppShimsDisabledForTest() && !g_app_shims_allow_update_and_launch_in_tests) { return; @@ -1153,7 +1153,7 @@ const extensions::FileHandlersInfo& file_handlers_info, const ShortcutLocations& creation_locations, ShortcutCreationReason creation_reason) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE)); + DCHECK_CURRENTLY_ON(content::BrowserThread::FILE); if (AppShimsDisabledForTest()) return true; @@ -1164,7 +1164,7 @@ void DeletePlatformShortcuts(const base::FilePath& app_data_path, scoped_ptr<ShortcutInfo> shortcut_info) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE)); + DCHECK_CURRENTLY_ON(content::BrowserThread::FILE); WebAppShortcutCreator shortcut_creator(app_data_path, shortcut_info.get(), extensions::FileHandlersInfo()); shortcut_creator.DeleteShortcuts();
diff --git a/chrome/browser/web_applications/web_app_win.cc b/chrome/browser/web_applications/web_app_win.cc index 2630c61..b9cd61e2 100644 --- a/chrome/browser/web_applications/web_app_win.cc +++ b/chrome/browser/web_applications/web_app_win.cc
@@ -287,7 +287,7 @@ const base::string16& title, bool* was_pinned_to_taskbar, std::vector<base::FilePath>* shortcut_paths) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE)); + DCHECK(content::BrowserThread::FILE); // Get all possible locations for shortcuts. web_app::ShortcutLocations all_shortcut_locations; @@ -584,7 +584,7 @@ const extensions::FileHandlersInfo& file_handlers_info, const ShortcutLocations& creation_locations, ShortcutCreationReason creation_reason) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE)); + DCHECK_CURRENTLY_ON(content::BrowserThread::FILE); // Nothing to do on Windows for hidden apps. if (creation_locations.applications_menu_location == APP_MENU_LOCATION_HIDDEN) @@ -636,7 +636,7 @@ const base::string16& old_app_title, scoped_ptr<ShortcutInfo> shortcut_info, const extensions::FileHandlersInfo& file_handlers_info) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE)); + DCHECK_CURRENTLY_ON(content::BrowserThread::FILE); // Generates file name to use with persisted ico and shortcut file. base::FilePath file_name =
diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi index 82d534d4..0ba338a3 100644 --- a/chrome/chrome_browser.gypi +++ b/chrome/chrome_browser.gypi
@@ -174,8 +174,8 @@ 'browser/android/tab_state.h', 'browser/android/thumbnail/thumbnail.cc', 'browser/android/thumbnail/thumbnail.h', - 'browser/android/thumbnail/thumbnail_store.cc', - 'browser/android/thumbnail/thumbnail_store.h', + 'browser/android/thumbnail/thumbnail_cache.cc', + 'browser/android/thumbnail/thumbnail_cache.h', 'browser/android/transition_page_helper.cc', 'browser/android/transition_page_helper.h', 'browser/android/url_utilities.cc', @@ -1336,6 +1336,14 @@ 'browser/content_settings/web_site_settings_uma_util.cc', 'browser/content_settings/web_site_settings_uma_util.h', ], + 'chrome_browser_engagement_sources': [ + 'browser/engagement/site_engagement_helper.cc', + 'browser/engagement/site_engagement_helper.h', + 'browser/engagement/site_engagement_service.cc', + 'browser/engagement/site_engagement_service.h', + 'browser/engagement/site_engagement_service_factory.cc', + 'browser/engagement/site_engagement_service_factory.h', + ], 'chrome_browser_extensions_sources': [ 'browser/accessibility/accessibility_extension_api.cc', 'browser/accessibility/accessibility_extension_api.h', @@ -1552,6 +1560,8 @@ 'browser/favicon/favicon_helper.h', 'browser/favicon/favicon_service_factory.cc', 'browser/favicon/favicon_service_factory.h', + 'browser/favicon/large_icon_service_factory.cc', + 'browser/favicon/large_icon_service_factory.h', ], 'chrome_browser_gnome_keyring_sources': [ 'browser/password_manager/native_backend_gnome_x.cc', @@ -2528,6 +2538,10 @@ 'browser/services/gcm/gcm_profile_service.h', 'browser/services/gcm/gcm_profile_service_factory.cc', 'browser/services/gcm/gcm_profile_service_factory.h', + 'browser/services/gcm/instance_id/instance_id_profile_service.cc', + 'browser/services/gcm/instance_id/instance_id_profile_service.h', + 'browser/services/gcm/instance_id/instance_id_profile_service_factory.cc', + 'browser/services/gcm/instance_id/instance_id_profile_service_factory.h', ], 'chrome_browser_session_sources': [ 'browser/sessions/base_session_service_delegate.h', @@ -3141,6 +3155,7 @@ '<@(chrome_browser_bookmark_sources)', '<@(chrome_browser_browser_process_sources)', '<@(chrome_browser_content_settings_sources)', + '<@(chrome_browser_engagement_sources)', '<@(chrome_browser_favicon_sources)', '<@(chrome_browser_google_sources)', '<@(chrome_browser_history_sources)',
diff --git a/chrome/chrome_browser_ui.gypi b/chrome/chrome_browser_ui.gypi index 7f895c5..3331795 100644 --- a/chrome/chrome_browser_ui.gypi +++ b/chrome/chrome_browser_ui.gypi
@@ -1979,6 +1979,8 @@ 'browser/ui/views/apps/app_window_desktop_native_widget_aura_win.h', 'browser/ui/views/apps/app_window_desktop_window_tree_host_win.cc', 'browser/ui/views/apps/app_window_desktop_window_tree_host_win.h', + 'browser/ui/views/apps/app_window_easy_resize_window_targeter.cc', + 'browser/ui/views/apps/app_window_easy_resize_window_targeter.h', 'browser/ui/views/apps/chrome_app_window_client_views_win.cc', 'browser/ui/views/apps/chrome_native_app_window_views_aura.cc', 'browser/ui/views/apps/chrome_native_app_window_views_aura.h',
diff --git a/chrome/chrome_common.gypi b/chrome/chrome_common.gypi index 2f7ccad..7faa5e6 100644 --- a/chrome/chrome_common.gypi +++ b/chrome/chrome_common.gypi
@@ -634,45 +634,33 @@ }, { # OS == ios 'sources!': [ 'common/net/net_resource_provider.cc', + ], + }], + ['OS == "android" or OS == "ios"', { + 'sources!': [ 'common/net/x509_certificate_model.cc', ], }], - ['os_posix == 1 and OS != "mac" and OS != "ios" and OS != "android"', { - 'dependencies': [ - '../build/linux/system.gyp:ssl', - ], - }, - ], - ['os_posix != 1 or OS == "mac" or OS == "ios"', { - 'sources!': [ - 'common/net/x509_certificate_model_nss.cc', - 'common/net/x509_certificate_model_openssl.cc', - ], - }, - ], - ['OS == "android"', { - 'dependencies': [ - '../third_party/boringssl/boringssl.gyp:boringssl', - ], - 'sources!': [ - 'common/net/x509_certificate_model.cc', - 'common/net/x509_certificate_model_openssl.cc', - ], - }], - ['use_openssl==1', { - 'sources!': [ - 'common/net/x509_certificate_model_nss.cc', - ], + ['use_openssl_certs == 1 and OS != "android"', { 'dependencies': [ '<(DEPTH)/third_party/boringssl/boringssl.gyp:boringssl', ], - }, - { # else !use_openssl: remove the unneeded files + }, { 'sources!': [ 'common/net/x509_certificate_model_openssl.cc', ], }, ], + ['use_nss_certs == 1', { + 'dependencies': [ + '../build/linux/system.gyp:ssl', + ], + }, { + 'sources!': [ + 'common/net/x509_certificate_model_nss.cc', + ], + }, + ], ['OS=="win"', { # TODO(jschuh): crbug.com/167187 fix size_t to int truncations. 'msvs_disabled_warnings': [4267, ],
diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi index fdc032a..190001f 100644 --- a/chrome/chrome_tests.gypi +++ b/chrome/chrome_tests.gypi
@@ -292,6 +292,7 @@ 'browser/history/history_browsertest.cc', 'browser/history/redirect_browsertest.cc', 'browser/iframe_browsertest.cc', + 'browser/image_decoder_browsertest.cc', 'browser/importer/firefox_importer_browsertest.cc', 'browser/importer/ie_importer_browsertest_win.cc', 'browser/importer/importer_unittest_utils.cc', @@ -459,6 +460,7 @@ 'browser/ui/cocoa/content_settings/content_setting_bubble_cocoa_unittest.mm', 'browser/ui/cocoa/dev_tools_controller_browsertest.mm', 'browser/ui/cocoa/extensions/extension_install_dialog_controller_browsertest.mm', + 'browser/ui/cocoa/extensions/extension_message_bubble_browsertest_mac.mm', 'browser/ui/cocoa/extensions/extension_install_prompt_test_utils.h', 'browser/ui/cocoa/extensions/extension_install_prompt_test_utils.mm', 'browser/ui/cocoa/extensions/media_galleries_dialog_cocoa_browsertest.mm', @@ -474,6 +476,8 @@ 'browser/ui/cocoa/view_id_util_browsertest.mm', 'browser/ui/content_settings/content_setting_bubble_model_browsertest.cc', 'browser/ui/exclusive_access/fullscreen_controller_browsertest.cc', + 'browser/ui/extensions/extension_message_bubble_browsertest.cc', + 'browser/ui/extensions/extension_message_bubble_browsertest.h', 'browser/ui/extensions/bookmark_app_browsertest.cc', 'browser/ui/find_bar/find_bar_host_browsertest.cc', 'browser/ui/global_error/global_error_service_browsertest.cc', @@ -727,6 +731,8 @@ 'browser/chromeos/login/wizard_controller_browsertest.cc', 'browser/chromeos/memory/oom_priority_manager_browsertest.cc', 'browser/chromeos/net/network_portal_detector_impl_browsertest.cc', + 'browser/chromeos/ownership/fake_owner_settings_service.cc', + 'browser/chromeos/ownership/fake_owner_settings_service.h', 'browser/chromeos/policy/blocking_login_browsertest.cc', 'browser/chromeos/policy/device_cloud_policy_browsertest.cc', 'browser/chromeos/policy/device_local_account_browsertest.cc', @@ -740,12 +746,15 @@ 'browser/chromeos/policy/login_screen_default_policy_browsertest.cc', 'browser/chromeos/policy/policy_cert_verifier_browsertest.cc', 'browser/chromeos/policy/power_policy_browsertest.cc', + 'browser/chromeos/policy/restore_on_startup_browsertest_chromeos.cc', 'browser/chromeos/policy/user_cloud_external_data_manager_browsertest.cc', 'browser/chromeos/policy/user_cloud_policy_manager_chromeos_browsertest.cc', 'browser/chromeos/policy/variations_service_policy_browsertest.cc', 'browser/chromeos/power/peripheral_battery_observer_browsertest.cc', 'browser/chromeos/preferences_browsertest.cc', 'browser/chromeos/profiles/profile_helper_browsertest.cc', + 'browser/chromeos/settings/scoped_cros_settings_test_helper.cc', + 'browser/chromeos/settings/scoped_cros_settings_test_helper.h', 'browser/chromeos/shutdown_policy_browsertest.cc', 'browser/chromeos/system/device_disabling_browsertest.cc', 'browser/chromeos/system/tray_accessibility_browsertest.cc',
diff --git a/chrome/chrome_tests_unit.gypi b/chrome/chrome_tests_unit.gypi index 948a773..a5ac421 100644 --- a/chrome/chrome_tests_unit.gypi +++ b/chrome/chrome_tests_unit.gypi
@@ -94,6 +94,7 @@ 'browser/download/download_ui_controller_unittest.cc', 'browser/enumerate_modules_model_unittest_win.cc', 'browser/external_protocol/external_protocol_handler_unittest.cc', + 'browser/favicon/chrome_fallback_icon_client_unittest.cc', 'browser/file_select_helper_unittest.cc', 'browser/geolocation/geolocation_permission_context_unittest.cc', 'browser/global_keyboard_shortcuts_mac_unittest.mm', @@ -1300,6 +1301,8 @@ 'browser/chromeos/settings/device_oauth2_token_service_unittest.cc', 'browser/chromeos/settings/device_settings_provider_unittest.cc', 'browser/chromeos/settings/device_settings_service_unittest.cc', + 'browser/chromeos/settings/scoped_cros_settings_test_helper.cc', + 'browser/chromeos/settings/scoped_cros_settings_test_helper.h', 'browser/chromeos/settings/session_manager_operation_unittest.cc', 'browser/chromeos/settings/shutdown_policy_handler_unittest.cc', 'browser/chromeos/settings/stub_cros_settings_provider_unittest.cc', @@ -1426,6 +1429,7 @@ 'browser/diagnostics/diagnostics_model_unittest.cc', 'browser/download/download_commands_unittest.cc', 'browser/download/download_shelf_unittest.cc', + 'browser/engagement/site_engagement_service_unittest.cc', 'browser/first_run/first_run_unittest.cc', 'browser/font_family_cache_unittest.cc', 'browser/importer/firefox_profile_lock_unittest.cc',
diff --git a/chrome/chrome_utility.gypi b/chrome/chrome_utility.gypi index d076a1b..08d58b24 100644 --- a/chrome/chrome_utility.gypi +++ b/chrome/chrome_utility.gypi
@@ -133,20 +133,6 @@ }, }, }], - ['OS!="win" and OS!="mac" and use_openssl==1', { - 'sources!': [ - 'utility/importer/nss_decryptor.cc', - ] - }], - ['OS!="win" and OS!="mac" and use_openssl==0', { - 'dependencies': [ - '../crypto/crypto.gyp:crypto', - ], - 'sources': [ - 'utility/importer/nss_decryptor_system_nss.cc', - 'utility/importer/nss_decryptor_system_nss.h', - ], - }], ['OS!="android"', { 'dependencies': [ '../net/net.gyp:net_utility_services', @@ -155,6 +141,15 @@ '<@(chrome_utility_importer_sources)', ], }], + ['use_nss_certs==1', { + 'dependencies': [ + '../crypto/crypto.gyp:crypto', + ], + 'sources': [ + 'utility/importer/nss_decryptor_system_nss.cc', + 'utility/importer/nss_decryptor_system_nss.h', + ], + }], ['OS=="android" and use_seccomp_bpf==1', { 'dependencies': [ '../sandbox/sandbox.gyp:seccomp_bpf',
diff --git a/chrome/common/chrome_switches.cc b/chrome/common/chrome_switches.cc index 0ddcae1..fe6b9416 100644 --- a/chrome/common/chrome_switches.cc +++ b/chrome/common/chrome_switches.cc
@@ -342,6 +342,10 @@ // Disables using bubbles for session restore request. const char kDisableSessionCrashedBubble[] = "disable-session-crashed-bubble"; +// Disables the Site Engagement service, which records interaction with sites +// and allocates certain resources accordingly. +const char kDisableSiteEngagementService[] = "disable-site-engagement-service"; + // Disables the suggestions service. const char kDisableSuggestionsService[] = "disable-suggestions-service"; @@ -552,6 +556,10 @@ const char kEnableSettingsWindow[] = "enable-settings-window"; const char kDisableSettingsWindow[] = "disable-settings-window"; +// Enable the Site Engagement service, which records interaction with sites and +// allocates certain resources accordingly. +const char kEnableSiteEngagementService[] = "enable-site-engagement-service"; + // Enable SPDY/4, aka HTTP/2. This is a temporary testing flag. const char kEnableSpdy4[] = "enable-spdy4";
diff --git a/chrome/common/chrome_switches.h b/chrome/common/chrome_switches.h index c70f7f1..e12d932 100644 --- a/chrome/common/chrome_switches.h +++ b/chrome/common/chrome_switches.h
@@ -101,6 +101,7 @@ extern const char kDisableSavePasswordBubble[]; extern const char kDisableSdchPersistence[]; extern const char kDisableSessionCrashedBubble[]; +extern const char kDisableSiteEngagementService[]; extern const char kDisableSuggestionsService[]; extern const char kDisableSync[]; extern const char kDisableSyncTypes[]; @@ -161,6 +162,7 @@ extern const char kEnableSessionCrashedBubble[]; extern const char kEnableSettingsWindow[]; extern const char kDisableSettingsWindow[]; +extern const char kEnableSiteEngagementService[]; extern const char kEnableSpdy4[]; extern const char kEnableSuggestionsService[]; extern const char kEnableSupervisedUserManagedBookmarksFolder[];
diff --git a/chrome/common/chrome_utility_messages.h b/chrome/common/chrome_utility_messages.h index 70c5aa60..cd828af 100644 --- a/chrome/common/chrome_utility_messages.h +++ b/chrome/common/chrome_utility_messages.h
@@ -284,6 +284,4 @@ // of kernel support for seccomp-bpf. IPC_MESSAGE_CONTROL1(ChromeUtilityHostMsg_DetectSeccompSupport_ResultPrctl, bool /* seccomp prctl supported */) -IPC_MESSAGE_CONTROL1(ChromeUtilityHostMsg_DetectSeccompSupport_ResultSyscall, - bool /* seccomp syscall supported */) #endif
diff --git a/chrome/common/extensions/api/developer_private.idl b/chrome/common/extensions/api/developer_private.idl index a1d817b..276cf64 100644 --- a/chrome/common/extensions/api/developer_private.idl +++ b/chrome/common/extensions/api/developer_private.idl
@@ -284,7 +284,8 @@ VIEW_REGISTERED, // window / view closed. VIEW_UNREGISTERED, - ERROR_ADDED + ERROR_ADDED, + PREFS_CHANGED }; dictionary PackDirectoryResponse {
diff --git a/chrome/common/extensions/api/webview_tag.json b/chrome/common/extensions/api/webview_tag.json index 5485f6d..30603b0 100644 --- a/chrome/common/extensions/api/webview_tag.json +++ b/chrome/common/extensions/api/webview_tag.json
@@ -30,7 +30,7 @@ "description": "A set of data types. Missing properties are interpreted as <code>false</code>.", "properties": { "appcache": { "type": "boolean", "optional": true, "description": "Websites' appcaches." }, - "cache": { "type": "boolean", "optional": true, "description": "The browser's cache. Note: when removing data, this clears the entire cache; it is not limited to the range you specify." }, + "cache": { "type": "boolean", "optional": true, "description": "Since Chrome 43.<br>The browser's cache. Note: when removing data, this clears the entire cache; it is not limited to the range you specify." }, "cookies": { "type": "boolean", "optional": true, "description": "The partition's cookies." }, "fileSystems": { "type": "boolean", "optional": true, "description": "Websites' filesystems." }, "indexedDB": { "type": "boolean", "optional": true, "description": "Websites' IndexedDB data." },
diff --git a/chrome/common/favicon/fallback_icon_url_parser_unittest.cc b/chrome/common/favicon/fallback_icon_url_parser_unittest.cc index f22bb95..2d88f3e 100644 --- a/chrome/common/favicon/fallback_icon_url_parser_unittest.cc +++ b/chrome/common/favicon/fallback_icon_url_parser_unittest.cc
@@ -20,11 +20,11 @@ // Default values for FallbackIconStyle, from // /components/favicon_base/fallback_icon_style.h -SkColor kDefaultBackgroundColor = SkColorSetRGB(0x80, 0x80, 0x80); -SkColor kDefaultTextColorDark = SK_ColorBLACK; -SkColor kDefaultTextColorLight = SK_ColorWHITE; -double kDefaultFontSizeRatio = 0.8; -double kDefaultRoundness = 0.125; // 1 / 8. +const SkColor kDefaultBackgroundColor = SkColorSetRGB(0x78, 0x78, 0x78); +const SkColor kDefaultTextColorDark = SK_ColorBLACK; +const SkColor kDefaultTextColorLight = SK_ColorWHITE; +const double kDefaultFontSizeRatio = 0.44; +const double kDefaultRoundness = 0; const char kTestUrlStr[] = "https://www.google.ca/imghp?hl=en&tab=wi";
diff --git a/chrome/common/net/BUILD.gn b/chrome/common/net/BUILD.gn index b69512ca..b73de58 100644 --- a/chrome/common/net/BUILD.gn +++ b/chrome/common/net/BUILD.gn
@@ -30,31 +30,25 @@ ] if (is_ios) { - sources -= [ - "net_resource_provider.cc", - "x509_certificate_model.cc", - ] + sources -= [ "net_resource_provider.cc" ] } else { deps += [ "//gpu/ipc" ] } - if (is_win || is_mac || is_ios) { - sources -= [ - "x509_certificate_model_nss.cc", - "x509_certificate_model_openssl.cc", - ] - } else if (use_openssl) { - sources -= [ "x509_certificate_model_nss.cc" ] + if (is_android || is_ios) { + sources -= [ "x509_certificate_model.cc" ] + } + + if (use_openssl_certs && !is_android) { + deps += [ "//third_party/boringssl" ] } else { sources -= [ "x509_certificate_model_openssl.cc" ] } - if (is_android) { - sources -= [ - "x509_certificate_model.cc", - "x509_certificate_model_openssl.cc", - ] - deps += [ "//third_party/boringssl" ] + if (use_nss_certs) { + deps += [ "//crypto:platform" ] + } else { + sources -= [ "x509_certificate_model_nss.cc" ] } configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
diff --git a/chrome/common/origin_util.cc b/chrome/common/origin_util.cc index 2884d74e..b2f7d63 100644 --- a/chrome/common/origin_util.cc +++ b/chrome/common/origin_util.cc
@@ -10,7 +10,7 @@ #include "url/gurl.h" bool IsOriginSecure(const GURL& url) { - if (url.SchemeUsesTLS() || url.SchemeIsFile()) + if (url.SchemeIsCryptographic() || url.SchemeIsFile()) return true; if (url.SchemeIsFileSystem() && url.inner_url() &&
diff --git a/chrome/common/partial_circular_buffer.cc b/chrome/common/partial_circular_buffer.cc index 4161cc1..6e3e017e 100644 --- a/chrome/common/partial_circular_buffer.cc +++ b/chrome/common/partial_circular_buffer.cc
@@ -139,30 +139,49 @@ void PartialCircularBuffer::Write(const void* buffer, uint32 buffer_size) { DCHECK(buffer_data_); - uint32 position_before_write = position_; + const uint8* input = static_cast<const uint8*>(buffer); + uint32 wrap_position = buffer_data_->wrap_position; + uint32 cycle_size = data_size_ - wrap_position; - uint32 to_eof = data_size_ - position_; - uint32 to_write = std::min(buffer_size, to_eof); - DoWrite(buffer_data_->data + position_, buffer, to_write); - if (position_ >= data_size_) { - DCHECK_EQ(position_, data_size_); - position_ = buffer_data_->wrap_position; + // First write the non-wrapping part. + if (position_ < wrap_position) { + uint32 space_left = wrap_position - position_; + uint32 write_size = std::min(buffer_size, space_left); + DoWrite(input, write_size); + input += write_size; + buffer_size -= write_size; } - if (to_write < buffer_size) { - uint32 remainder_to_write = buffer_size - to_write; - DCHECK_LT(position_, position_before_write); - DCHECK_LE(position_ + remainder_to_write, position_before_write); - DoWrite(buffer_data_->data + position_, - reinterpret_cast<const uint8*>(buffer) + to_write, - remainder_to_write); + // Skip the part that would overlap. + if (buffer_size > cycle_size) { + uint32 skip = buffer_size - cycle_size; + input += skip; + buffer_size -= skip; + position_ = wrap_position + (position_ - wrap_position + skip) % cycle_size; } + + // Finally write the wrapping part. + DoWrite(input, buffer_size); } -void PartialCircularBuffer::DoWrite(void* dest, const void* src, uint32 num) { - memcpy(dest, src, num); - position_ += num; +void PartialCircularBuffer::DoWrite(const uint8* input, uint32 input_size) { + DCHECK_LT(position_, data_size_); buffer_data_->total_written = - std::min(buffer_data_->total_written + num, data_size_); + std::min(buffer_data_->total_written + input_size, data_size_); + + // Write() skips any overlapping part, so this loop will run at most twice. + while (input_size > 0) { + uint32 space_left = data_size_ - position_; + uint32 write_size = std::min(input_size, space_left); + memcpy(buffer_data_->data + position_, input, write_size); + input += write_size; + input_size -= write_size; + position_ += write_size; + if (position_ >= data_size_) { + DCHECK_EQ(position_, data_size_); + position_ = buffer_data_->wrap_position; + } + } + buffer_data_->end_position = position_; }
diff --git a/chrome/common/partial_circular_buffer.h b/chrome/common/partial_circular_buffer.h index 1f986cc..e1fb118 100644 --- a/chrome/common/partial_circular_buffer.h +++ b/chrome/common/partial_circular_buffer.h
@@ -52,7 +52,7 @@ }; #pragma pack(pop) - void DoWrite(void* dest, const void* src, uint32 num); + void DoWrite(const uint8* input, uint32 input_size); // Used for reading and writing. BufferData* buffer_data_;
diff --git a/chrome/common/partial_circular_buffer_unittest.cc b/chrome/common/partial_circular_buffer_unittest.cc index 85153fa..701aefa 100644 --- a/chrome/common/partial_circular_buffer_unittest.cc +++ b/chrome/common/partial_circular_buffer_unittest.cc
@@ -189,3 +189,24 @@ EXPECT_EQ(0u, pcb_read_->Read(output_data, sizeof(output_data))); } + +TEST_F(PartialCircularBufferTest, WrapTwiceWithSingleWrite) { + const size_t kInputSize = sizeof(kInputData); + const size_t kLargeSize = kInputSize * 7; + uint8 large_input[kLargeSize] = {0}; + for (size_t offset = 0; offset < kLargeSize; offset += kInputSize) + memcpy(large_input + offset, kInputData, kInputSize); + + InitWriteBuffer(false); + pcb_write_->Write(large_input, kLargeSize); + InitReadBuffer(); + + uint8 output_data[sizeof(kOutputRefDataWrap)] = {0}; + EXPECT_EQ(sizeof(output_data), + pcb_read_->Read(output_data, sizeof(output_data))); + + EXPECT_EQ(0, memcmp(kOutputRefDataWrap, output_data, sizeof(output_data))); + + EXPECT_EQ(0u, pcb_read_->Read(output_data, sizeof(output_data))); +} +
diff --git a/chrome/common/pref_names.cc b/chrome/common/pref_names.cc index 8f874ca..eca2d70 100644 --- a/chrome/common/pref_names.cc +++ b/chrome/common/pref_names.cc
@@ -1915,6 +1915,10 @@ // A boolean pref. If set to true, experimental webview based signin flow // is deactivated. const char kWebviewSigninDisabled[] = "webview_signin_disabled"; + +// A boolean pref. If set to true, then on the network screen we should display +// whether the WebView-based sign-in flow is active. +const char kNewLoginUIPopup[] = "new_login_ui_popup"; #endif // defined(OS_CHROMEOS) // Whether there is a Flash version installed that supports clearing LSO data.
diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h index 2d5e3098..6344211 100644 --- a/chrome/common/pref_names.h +++ b/chrome/common/pref_names.h
@@ -677,6 +677,7 @@ extern const char kNewOobe[]; extern const char kConsumerManagementEnrollmentStage[]; extern const char kWebviewSigninDisabled[]; +extern const char kNewLoginUIPopup[]; #endif // defined(OS_CHROMEOS) extern const char kClearPluginLSODataEnabled[];
diff --git a/chrome/installer/linux/rpm/expected_deps_i386 b/chrome/installer/linux/rpm/expected_deps_i386 index 698b085..00900a9 100644 --- a/chrome/installer/linux/rpm/expected_deps_i386 +++ b/chrome/installer/linux/rpm/expected_deps_i386
@@ -60,6 +60,7 @@ libstdc++.so.6(GLIBCXX_3.4.10) libstdc++.so.6(GLIBCXX_3.4.11) libstdc++.so.6(GLIBCXX_3.4.15) +libstdc++.so.6(GLIBCXX_3.4.5) libstdc++.so.6(GLIBCXX_3.4.9) libX11.so.6 libXcomposite.so.1
diff --git a/chrome/installer/linux/rpm/expected_deps_x86_64 b/chrome/installer/linux/rpm/expected_deps_x86_64 index 7e9538d..1b5cd370 100644 --- a/chrome/installer/linux/rpm/expected_deps_x86_64 +++ b/chrome/installer/linux/rpm/expected_deps_x86_64
@@ -48,6 +48,7 @@ libstdc++.so.6(GLIBCXX_3.4.10)(64bit) libstdc++.so.6(GLIBCXX_3.4.11)(64bit) libstdc++.so.6(GLIBCXX_3.4.15)(64bit) +libstdc++.so.6(GLIBCXX_3.4.5)(64bit) libstdc++.so.6(GLIBCXX_3.4)(64bit) libstdc++.so.6(GLIBCXX_3.4.9)(64bit) libX11.so.6()(64bit)
diff --git a/chrome/renderer/chrome_render_frame_observer.cc b/chrome/renderer/chrome_render_frame_observer.cc index a67f8fcc..c65fc50 100644 --- a/chrome/renderer/chrome_render_frame_observer.cc +++ b/chrome/renderer/chrome_render_frame_observer.cc
@@ -197,8 +197,10 @@ void ChromeRenderFrameObserver::OnAppBannerPromptRequest( int request_id, const std::string& platform) { blink::WebAppBannerPromptReply reply = blink::WebAppBannerPromptReply::None; + blink::WebString web_platform(base::UTF8ToUTF16(platform)); + blink::WebVector<blink::WebString> web_platforms(&web_platform, 1); render_frame()->GetWebFrame()->willShowInstallBannerPrompt( - blink::WebString(base::UTF8ToUTF16(platform)), &reply); + web_platforms, &reply); Send(new ChromeViewHostMsg_AppBannerPromptReply( routing_id(), request_id, reply));
diff --git a/chrome/renderer/content_settings_observer.cc b/chrome/renderer/content_settings_observer.cc index 5e7c96a..3769a85cc 100644 --- a/chrome/renderer/content_settings_observer.cc +++ b/chrome/renderer/content_settings_observer.cc
@@ -11,7 +11,7 @@ #include "content/public/renderer/document_state.h" #include "content/public/renderer/render_frame.h" #include "content/public/renderer/render_view.h" -#include "third_party/WebKit/public/platform/WebPermissionCallbacks.h" +#include "third_party/WebKit/public/platform/WebContentSettingCallbacks.h" #include "third_party/WebKit/public/platform/WebURL.h" #include "third_party/WebKit/public/web/WebDataSource.h" #include "third_party/WebKit/public/web/WebDocument.h" @@ -30,10 +30,10 @@ #include "extensions/renderer/dispatcher.h" #endif +using blink::WebContentSettingCallbacks; using blink::WebDataSource; using blink::WebDocument; using blink::WebFrame; -using blink::WebPermissionCallbacks; using blink::WebSecurityOrigin; using blink::WebString; using blink::WebURL; @@ -285,11 +285,11 @@ } void ContentSettingsObserver::requestFileSystemAccessAsync( - const WebPermissionCallbacks& callbacks) { + const WebContentSettingCallbacks& callbacks) { WebFrame* frame = render_frame()->GetWebFrame(); if (frame->securityOrigin().isUnique() || frame->top()->securityOrigin().isUnique()) { - WebPermissionCallbacks permissionCallbacks(callbacks); + WebContentSettingCallbacks permissionCallbacks(callbacks); permissionCallbacks.doDeny(); return; } @@ -628,7 +628,7 @@ if (it == permission_requests_.end()) return; - WebPermissionCallbacks callbacks = it->second; + WebContentSettingCallbacks callbacks = it->second; permission_requests_.erase(it); if (allowed) {
diff --git a/chrome/renderer/content_settings_observer.h b/chrome/renderer/content_settings_observer.h index 736fd0da..6e21be7 100644 --- a/chrome/renderer/content_settings_observer.h +++ b/chrome/renderer/content_settings_observer.h
@@ -59,31 +59,31 @@ // blink::WebContentSettingsClient implementation. virtual bool allowDatabase(const blink::WebString& name, const blink::WebString& display_name, - unsigned long estimated_size) override; + unsigned long estimated_size); virtual void requestFileSystemAccessAsync( - const blink::WebPermissionCallbacks& callbacks) override; + const blink::WebContentSettingCallbacks& callbacks); virtual bool allowImage(bool enabled_per_settings, - const blink::WebURL& image_url) override; + const blink::WebURL& image_url); virtual bool allowIndexedDB(const blink::WebString& name, - const blink::WebSecurityOrigin& origin) override; - virtual bool allowPlugins(bool enabled_per_settings) override; - virtual bool allowScript(bool enabled_per_settings) override; + const blink::WebSecurityOrigin& origin); + virtual bool allowPlugins(bool enabled_per_settings); + virtual bool allowScript(bool enabled_per_settings); virtual bool allowScriptFromSource(bool enabled_per_settings, - const blink::WebURL& script_url) override; - virtual bool allowStorage(bool local) override; - virtual bool allowReadFromClipboard(bool default_value) override; - virtual bool allowWriteToClipboard(bool default_value) override; - virtual bool allowMutationEvents(bool default_value) override; - virtual void didNotAllowPlugins() override; - virtual void didNotAllowScript() override; + const blink::WebURL& script_url); + virtual bool allowStorage(bool local); + virtual bool allowReadFromClipboard(bool default_value); + virtual bool allowWriteToClipboard(bool default_value); + virtual bool allowMutationEvents(bool default_value); + virtual void didNotAllowPlugins(); + virtual void didNotAllowScript(); virtual bool allowDisplayingInsecureContent( bool allowed_per_settings, const blink::WebSecurityOrigin& context, - const blink::WebURL& url) override; + const blink::WebURL& url); virtual bool allowRunningInsecureContent( bool allowed_per_settings, const blink::WebSecurityOrigin& context, - const blink::WebURL& url) override; + const blink::WebURL& url); // This is used for cases when the NPAPI plugins malfunction if used. bool AreNPAPIPluginsBlocked() const; @@ -159,7 +159,7 @@ bool npapi_plugins_blocked_; int current_request_id_; - typedef std::map<int, blink::WebPermissionCallbacks> PermissionRequestMap; + typedef std::map<int, blink::WebContentSettingCallbacks> PermissionRequestMap; PermissionRequestMap permission_requests_; // If true, IsWhitelistedForContentSettings will always return true.
diff --git a/chrome/renderer/media/chrome_key_systems.cc b/chrome/renderer/media/chrome_key_systems.cc index 63d4734..7f4c8362 100644 --- a/chrome/renderer/media/chrome_key_systems.cc +++ b/chrome/renderer/media/chrome_key_systems.cc
@@ -87,11 +87,11 @@ info.max_video_robustness = media::EmeRobustness::EMPTY; // Persistent sessions are faked. - info.persistent_license_support = media::EME_SESSION_TYPE_SUPPORTED; + info.persistent_license_support = media::EmeSessionTypeSupport::SUPPORTED; info.persistent_release_message_support = - media::EME_SESSION_TYPE_NOT_SUPPORTED; - info.persistent_state_support = media::EME_FEATURE_REQUESTABLE; - info.distinctive_identifier_support = media::EME_FEATURE_NOT_SUPPORTED; + media::EmeSessionTypeSupport::NOT_SUPPORTED; + info.persistent_state_support = media::EmeFeatureSupport::REQUESTABLE; + info.distinctive_identifier_support = media::EmeFeatureSupport::NOT_SUPPORTED; info.pepper_type = kExternalClearKeyPepperType; @@ -193,20 +193,22 @@ cdm::AddWidevineWithCodecs( cdm::WIDEVINE, supported_codecs, #if defined(OS_CHROMEOS) - media::EmeRobustness::HW_SECURE_ALL, // Maximum audio robustness. - media::EmeRobustness::HW_SECURE_ALL, // Maximim video robustness. - // persistent-license. - media::EME_SESSION_TYPE_SUPPORTED_WITH_IDENTIFIER, - media::EME_SESSION_TYPE_NOT_SUPPORTED, // persistent-release-message. - media::EME_FEATURE_REQUESTABLE, // Persistent state. - media::EME_FEATURE_REQUESTABLE, // Distinctive identifier. + media::EmeRobustness::HW_SECURE_ALL, // Maximum audio robustness. + media::EmeRobustness::HW_SECURE_ALL, // Maximim video robustness. + media::EmeSessionTypeSupport:: + SUPPORTED_WITH_IDENTIFIER, // Persistent-license. + media::EmeSessionTypeSupport:: + NOT_SUPPORTED, // Persistent-release-message. + media::EmeFeatureSupport::REQUESTABLE, // Persistent state. + media::EmeFeatureSupport::REQUESTABLE, // Distinctive identifier. #else // (Desktop) - media::EmeRobustness::SW_SECURE_CRYPTO, // Maximum audio robustness. - media::EmeRobustness::SW_SECURE_DECODE, // Maximum video robustness. - media::EME_SESSION_TYPE_NOT_SUPPORTED, // persistent-license. - media::EME_SESSION_TYPE_NOT_SUPPORTED, // persistent-release-message. - media::EME_FEATURE_REQUESTABLE, // Persistent state. - media::EME_FEATURE_NOT_SUPPORTED, // Distinctive identifier. + media::EmeRobustness::SW_SECURE_CRYPTO, // Maximum audio robustness. + media::EmeRobustness::SW_SECURE_DECODE, // Maximum video robustness. + media::EmeSessionTypeSupport::NOT_SUPPORTED, // persistent-license. + media::EmeSessionTypeSupport:: + NOT_SUPPORTED, // persistent-release-message. + media::EmeFeatureSupport::REQUESTABLE, // Persistent state. + media::EmeFeatureSupport::NOT_SUPPORTED, // Distinctive identifier. #endif // defined(OS_CHROMEOS) concrete_key_systems); }
diff --git a/chrome/renderer/safe_browsing/phishing_classifier_delegate_browsertest.cc b/chrome/renderer/safe_browsing/phishing_classifier_delegate_browsertest.cc index 8f7f29884..50141be 100644 --- a/chrome/renderer/safe_browsing/phishing_classifier_delegate_browsertest.cc +++ b/chrome/renderer/safe_browsing/phishing_classifier_delegate_browsertest.cc
@@ -407,7 +407,14 @@ EXPECT_CALL(*classifier_, CancelPendingClassification()); } -IN_PROC_BROWSER_TEST_F(PhishingClassifierDelegateTest, NoScorer) { +// Flaky: crbug.com/479757 +#if defined(LEAK_SANITIZER) +#define MAYBE_NoScorer DISABLED_NoScorer +#else +#define MAYBE_NoScorer NoScorer +#endif + +IN_PROC_BROWSER_TEST_F(PhishingClassifierDelegateTest, MAYBE_NoScorer) { // For this test, we'll create the delegate with no scorer available yet. ASSERT_FALSE(classifier_->is_ready());
diff --git a/chrome/renderer/worker_content_settings_client_proxy.cc b/chrome/renderer/worker_content_settings_client_proxy.cc index 31d83d2..05c6b945 100644 --- a/chrome/renderer/worker_content_settings_client_proxy.cc +++ b/chrome/renderer/worker_content_settings_client_proxy.cc
@@ -9,7 +9,6 @@ #include "content/public/renderer/render_frame.h" #include "content/public/renderer/render_thread.h" #include "ipc/ipc_sync_message_filter.h" -#include "third_party/WebKit/public/platform/WebPermissionCallbacks.h" #include "third_party/WebKit/public/web/WebDocument.h" #include "third_party/WebKit/public/web/WebFrame.h" #include "third_party/WebKit/public/web/WebSecurityOrigin.h"
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn index cf446018..6a9af83 100644 --- a/chrome/test/BUILD.gn +++ b/chrome/test/BUILD.gn
@@ -380,6 +380,10 @@ "//third_party/wtl", "//ui/resources", ] + + configs -= [ "//build/config/win:default_incremental_linking" ] + configs += + [ "//build/config/win:default_large_module_incremental_linking" ] } if (is_mac) { @@ -737,6 +741,9 @@ deps += [ #'chrome.gyp:chrome_nacl_win64', TODO(GYP) ] + configs -= [ "//build/config/win:default_incremental_linking" ] + configs += + [ "//build/config/win:default_large_module_incremental_linking" ] } if (is_linux) { deps += [ @@ -1070,6 +1077,10 @@ "//third_party/wtl", "//ui/resources", ] + + configs -= [ "//build/config/win:default_incremental_linking" ] + configs += + [ "//build/config/win:default_large_module_incremental_linking" ] } else { sources -= [ "../app/chrome_version.rc.version" ] } @@ -1135,6 +1146,9 @@ "//third_party/wtl", "//ui/resources", ] + configs -= [ "//build/config/win:default_incremental_linking" ] + configs += + [ "//build/config/win:default_large_module_incremental_linking" ] } else { sources -= [ "../app/chrome_version.rc.version" ] } @@ -1604,6 +1618,10 @@ "//third_party/wtl", ] + configs -= [ "//build/config/win:default_incremental_linking" ] + configs += + [ "//build/config/win:default_large_module_incremental_linking" ] + libs = [ "comsupp.lib", "oleacc.lib", @@ -1718,7 +1736,11 @@ "//testing/perf", ] - if (!is_win) { + if (is_win) { + configs -= [ "//build/config/win:default_incremental_linking" ] + configs += + [ "//build/config/win:default_large_module_incremental_linking" ] + } else { sources -= [ "../app/chrome_command_ids.h",
diff --git a/chrome/test/base/testing_io_thread_state.cc b/chrome/test/base/testing_io_thread_state.cc index e79423f..30b90698 100644 --- a/chrome/test/base/testing_io_thread_state.cc +++ b/chrome/test/base/testing_io_thread_state.cc
@@ -79,7 +79,7 @@ } void TestingIOThreadState::Initialize(const base::Closure& done) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + DCHECK_CURRENTLY_ON(BrowserThread::IO); io_thread_state_->SetGlobalsForTesting(new IOThread::Globals()); @@ -87,7 +87,7 @@ } void TestingIOThreadState::Shutdown(const base::Closure& done) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + DCHECK_CURRENTLY_ON(BrowserThread::IO); delete io_thread_state_->globals(); io_thread_state_->SetGlobalsForTesting(NULL);
diff --git a/chrome/test/base/tracing.cc b/chrome/test/base/tracing.cc index f9d9f57a..2120b9f7 100644 --- a/chrome/test/base/tracing.cc +++ b/chrome/test/base/tracing.cc
@@ -55,7 +55,7 @@ virtual ~InProcessTraceController() {} bool BeginTracing(const std::string& category_patterns) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); return content::TracingController::GetInstance()->EnableRecording( base::trace_event::CategoryFilter(category_patterns), base::trace_event::TraceOptions(), @@ -66,7 +66,7 @@ const std::string& category_name, const std::string& event_name, int num_occurrences) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); DCHECK(num_occurrences > 0); watch_notification_count_ = num_occurrences; if (!content::TracingController::GetInstance()->SetWatchEvent( @@ -89,7 +89,7 @@ } bool WaitForWatchEvent(base::TimeDelta timeout) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); if (watch_notification_count_ == 0) return true; @@ -107,7 +107,7 @@ } bool EndTracing(std::string* json_trace_output) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); using namespace base::debug; if (!content::TracingController::GetInstance()->DisableRecording(
diff --git a/chrome/test/chromedriver/capabilities.cc b/chrome/test/chromedriver/capabilities.cc index 90e73ff9..33b34d2 100644 --- a/chrome/test/chromedriver/capabilities.cc +++ b/chrome/test/chromedriver/capabilities.cc
@@ -108,7 +108,9 @@ } capabilities->device_metrics.reset(device->device_metrics.release()); - capabilities->switches.SetSwitch("user-agent", device->user_agent); + // Don't override the user agent if blank (like for notebooks). + if (!device->user_agent.empty()) + capabilities->switches.SetSwitch("user-agent", device->user_agent); return Status(kOk); } @@ -139,13 +141,25 @@ int width = 0; int height = 0; double device_scale_factor = 0; + bool touch = true; + bool mobile = true; if (!metrics->GetInteger("width", &width) || !metrics->GetInteger("height", &height) || !metrics->GetDouble("pixelRatio", &device_scale_factor)) return Status(kUnknownError, "invalid 'deviceMetrics'"); + if (metrics->HasKey("touch")) { + if (!metrics->GetBoolean("touch", &touch)) + return Status(kUnknownError, "'touch' must be a boolean"); + } + + if (metrics->HasKey("mobile")) { + if (!metrics->GetBoolean("mobile", &mobile)) + return Status(kUnknownError, "'mobile' must be a boolean"); + } + DeviceMetrics* device_metrics = - new DeviceMetrics(width, height, device_scale_factor); + new DeviceMetrics(width, height, device_scale_factor, touch, mobile); capabilities->device_metrics = scoped_ptr<DeviceMetrics>(device_metrics); }
diff --git a/chrome/test/chromedriver/capabilities_unittest.cc b/chrome/test/chromedriver/capabilities_unittest.cc index b05aced..9b8a4e0ef8 100644 --- a/chrome/test/chromedriver/capabilities_unittest.cc +++ b/chrome/test/chromedriver/capabilities_unittest.cc
@@ -510,9 +510,9 @@ ASSERT_EQ(1u, capabilities.switches.GetSize()); ASSERT_TRUE(capabilities.switches.HasSwitch("user-agent")); ASSERT_EQ( - "Mozilla/5.0 (Linux; Android 4.2.1; en-us; Nexus 5 Build/JOP40D) " - "AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.166 " - "Mobile Safari/535.19", + "Mozilla/5.0 (Linux; Android 4.4.4; en-us; Nexus 5 Build/JOP40D) " + "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2307.2 " + "Mobile Safari/537.36", capabilities.switches.GetSwitchValue("user-agent")); ASSERT_EQ(360, capabilities.device_metrics->width);
diff --git a/chrome/test/chromedriver/chrome/device_metrics.cc b/chrome/test/chromedriver/chrome/device_metrics.cc index df43587..76d45eee 100644 --- a/chrome/test/chromedriver/chrome/device_metrics.cc +++ b/chrome/test/chromedriver/chrome/device_metrics.cc
@@ -4,11 +4,13 @@ #include "chrome/test/chromedriver/chrome/device_metrics.h" -DeviceMetrics::DeviceMetrics(int width, int height, double device_scale_factor) +DeviceMetrics::DeviceMetrics(int width, int height, double device_scale_factor, + bool touch, bool mobile) : width(width), height(height), device_scale_factor(device_scale_factor), - mobile(true), + touch(touch), + mobile(mobile), fit_window(false), text_autosizing(true), font_scale_factor(1) {}
diff --git a/chrome/test/chromedriver/chrome/device_metrics.h b/chrome/test/chromedriver/chrome/device_metrics.h index 1e238ef6..9af274c 100644 --- a/chrome/test/chromedriver/chrome/device_metrics.h +++ b/chrome/test/chromedriver/chrome/device_metrics.h
@@ -6,12 +6,14 @@ #define CHROME_TEST_CHROMEDRIVER_CHROME_DEVICE_METRICS_H_ struct DeviceMetrics { - DeviceMetrics(int width, int height, double device_scale_factor); + DeviceMetrics(int width, int height, double device_scale_factor, bool touch, + bool mobile); ~DeviceMetrics(); int width; int height; double device_scale_factor; + bool touch; bool mobile; bool fit_window; bool text_autosizing;
diff --git a/chrome/test/chromedriver/chrome/mobile_device.cc b/chrome/test/chromedriver/chrome/mobile_device.cc index 10b6dbb..683dd6c 100644 --- a/chrome/test/chromedriver/chrome/mobile_device.cc +++ b/chrome/test/chromedriver/chrome/mobile_device.cc
@@ -54,6 +54,8 @@ int width = 0; int height = 0; double device_scale_factor = 0.0; + bool touch = true; + bool mobile = true; if (!device->GetInteger("width", &width)) { return Status(kUnknownError, "malformed device width: should be an integer"); @@ -66,8 +68,16 @@ return Status(kUnknownError, "malformed device scale factor: should be a double"); } + if (!device->GetBoolean("touch", &touch)) { + return Status(kUnknownError, + "malformed touch: should be a bool"); + } + if (!device->GetBoolean("mobile", &mobile)) { + return Status(kUnknownError, + "malformed mobile: should be a bool"); + } tmp_mobile_device->device_metrics.reset( - new DeviceMetrics(width, height, device_scale_factor)); + new DeviceMetrics(width, height, device_scale_factor, touch, mobile)); *mobile_device = tmp_mobile_device.Pass(); return Status(kOk);
diff --git a/chrome/test/chromedriver/chrome/mobile_device_list.cc b/chrome/test/chromedriver/chrome/mobile_device_list.cc index 3c31b0f..aa4704b 100644 --- a/chrome/test/chromedriver/chrome/mobile_device_list.cc +++ b/chrome/test/chromedriver/chrome/mobile_device_list.cc
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// This file was generated at (2015-01-22 12:13:16.600114) by running: +// This file was generated at (2015-03-30 16:08:35.102812) by running: // chrome/test/chromedriver/embed_mobile_devices_in_cpp.py --directory // chrome/test/chromedriver/chrome/ // third_party/WebKit/Source/devtools/front_end/toolbox/OverridesUI.js @@ -10,158 +10,65 @@ #include "chrome/test/chromedriver/chrome/mobile_device_list.h" const char kMobileDevices[] = - "[{\"title\": \"Apple iPhone 3GS\", \"width\": 320, \"height\": 480, " - "\"deviceScaleFactor\": 1, \"userAgent\": \"Mozilla/5.0 (iPhone; U; CPU " + "[{\"title\": \"Apple iPhone 4\", \"width\": 320, \"height\": 480, " + "\"deviceScaleFactor\": 2, \"userAgent\": \"Mozilla/5.0 (iPhone; U; CPU " "iPhone OS 4_2_1 like Mac OS X; en-us) AppleWebKit/533.17.9 (KHTML, like " "Gecko) Version/5.0.2 Mobile/8C148 Safari/6533.18.5\", \"touch\": true, " - "\"mobile\": true},{\"title\": \"Apple iPhone 4\", \"width\": 320, " - "\"height\": 480, \"deviceScaleFactor\": 2, \"userAgent\": \"Mozilla/5.0 " - "(iPhone; U; CPU iPhone OS 4_2_1 like Mac OS X; en-us) " - "AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8C148 " - "Safari/6533.18.5\", \"touch\": true, \"mobile\": true},{\"title\": " - "\"Apple iPhone 5\", \"width\": 320, \"height\": 568, " - "\"deviceScaleFactor\": 2, \"userAgent\": \"Mozilla/5.0 (iPhone; CPU " - "iPhone OS 7_0 like Mac OS X; en-us) AppleWebKit/537.51.1 (KHTML, like " - "Gecko) Version/7.0 Mobile/11A465 Safari/9537.53\", \"touch\": true, " - "\"mobile\": true},{\"title\": \"Apple iPhone 6\", \"width\": 375, " - "\"height\": 667, \"deviceScaleFactor\": 2, \"userAgent\": \"Mozilla/5.0 " - "(iPhone; CPU iPhone OS 8_0 like Mac OS X) AppleWebKit/600.1.3 (KHTML, " - "like Gecko) Version/8.0 Mobile/12A4345d Safari/600.1.4\", \"touch\": " - "true, \"mobile\": true},{\"title\": \"Apple iPhone 6 Plus\", \"width\": " - "414, \"height\": 736, \"deviceScaleFactor\": 3, \"userAgent\": " + "\"mobile\": true},{\"title\": \"Apple iPhone 5\", \"width\": 320, " + "\"height\": 568, \"deviceScaleFactor\": 2, \"userAgent\": \"Mozilla/5.0 " + "(iPhone; CPU iPhone OS 7_0 like Mac OS X; en-us) AppleWebKit/537.51.1 " + "(KHTML, like Gecko) Version/7.0 Mobile/11A465 Safari/9537.53\", " + "\"touch\": true, \"mobile\": true},{\"title\": \"Apple iPhone 6\", " + "\"width\": 375, \"height\": 667, \"deviceScaleFactor\": 2, \"userAgent\": " "\"Mozilla/5.0 (iPhone; CPU iPhone OS 8_0 like Mac OS X) " "AppleWebKit/600.1.3 (KHTML, like Gecko) Version/8.0 Mobile/12A4345d " + "Safari/600.1.4\", \"touch\": true, \"mobile\": true},{\"title\": \"Apple " + "iPhone 6 Plus\", \"width\": 414, \"height\": 736, \"deviceScaleFactor\": " + "3, \"userAgent\": \"Mozilla/5.0 (iPhone; CPU iPhone OS 8_0 like Mac OS X) " + "AppleWebKit/600.1.3 (KHTML, like Gecko) Version/8.0 Mobile/12A4345d " "Safari/600.1.4\", \"touch\": true, \"mobile\": true},{\"title\": " - "\"BlackBerry Z10\", \"width\": 384, \"height\": 640, " - "\"deviceScaleFactor\": 2, \"userAgent\": \"Mozilla/5.0 (BB10; Touch) " - "AppleWebKit/537.10+ (KHTML, like Gecko) Version/10.0.9.2372 Mobile " - "Safari/537.10+\", \"touch\": true, \"mobile\": true},{\"title\": " "\"BlackBerry Z30\", \"width\": 360, \"height\": 640, " "\"deviceScaleFactor\": 2, \"userAgent\": \"Mozilla/5.0 (BB10; Touch) " "AppleWebKit/537.10+ (KHTML, like Gecko) Version/10.0.9.2372 Mobile " "Safari/537.10+\", \"touch\": true, \"mobile\": true},{\"title\": \"Google " "Nexus 4\", \"width\": 384, \"height\": 640, \"deviceScaleFactor\": 2, " - "\"userAgent\": \"Mozilla/5.0 (Linux; Android 4.2.1; en-us; Nexus 4 " - "Build/JOP40D) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.166 " - "Mobile Safari/535.19\", \"touch\": true, \"mobile\": true},{\"title\": " + "\"userAgent\": \"Mozilla/5.0 (Linux; Android 4.4.4; en-us; Nexus 4 " + "Build/JOP40D) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2307.2 " + "Mobile Safari/537.36\", \"touch\": true, \"mobile\": true},{\"title\": " "\"Google Nexus 5\", \"width\": 360, \"height\": 640, " "\"deviceScaleFactor\": 3, \"userAgent\": \"Mozilla/5.0 (Linux; Android " - "4.2.1; en-us; Nexus 5 Build/JOP40D) AppleWebKit/535.19 (KHTML, like " - "Gecko) Chrome/18.0.1025.166 Mobile Safari/535.19\", \"touch\": true, " - "\"mobile\": true},{\"title\": \"Google Nexus S\", \"width\": 320, " - "\"height\": 533, \"deviceScaleFactor\": 1.5, \"userAgent\": \"Mozilla/5.0 " - "(Linux; U; Android 2.3.4; en-us; Nexus S Build/GRJ22) AppleWebKit/533.1 " - "(KHTML, like Gecko) Version/4.0 Mobile Safari/533.1\", \"touch\": true, " - "\"mobile\": true},{\"title\": \"HTC Evo, Touch HD, Desire HD, Desire\", " - "\"width\": 320, \"height\": 533, \"deviceScaleFactor\": 1.5, " - "\"userAgent\": \"Mozilla/5.0 (Linux; U; Android 2.2; en-us; Sprint " - "APA9292KT Build/FRF91) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 " - "Mobile Safari/533.1\", \"touch\": true, \"mobile\": true},{\"title\": " - "\"HTC One X, EVO LTE\", \"width\": 360, \"height\": 640, " - "\"deviceScaleFactor\": 2, \"userAgent\": \"Mozilla/5.0 (Linux; Android " - "4.0.3; HTC One X Build/IML74K) AppleWebKit/535.19 (KHTML, like Gecko) " - "Chrome/18.0.1025.133 Mobile Safari/535.19\", \"touch\": true, \"mobile\": " - "true},{\"title\": \"HTC Sensation, Evo 3D\", \"width\": 360, \"height\": " - "640, \"deviceScaleFactor\": 1.5, \"userAgent\": \"Mozilla/5.0 (Linux; U; " - "Android 4.0.3; en-us; HTC Sensation Build/IML74K) AppleWebKit/534.30 " - "(KHTML, like Gecko) Version/4.0 Mobile Safari/534.30\", \"touch\": true, " - "\"mobile\": true},{\"title\": \"LG Optimus 2X, Optimus 3D, Optimus " - "Black\", \"width\": 320, \"height\": 533, \"deviceScaleFactor\": 1.5, " - "\"userAgent\": \"Mozilla/5.0 (Linux; U; Android 2.2; en-us; LG-P990/V08c " - "Build/FRG83) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile " - "Safari/533.1 MMS/LG-Android-MMS-V1.0/1.2\", \"touch\": true, \"mobile\": " - "true},{\"title\": \"LG Optimus G\", \"width\": 384, \"height\": 640, " - "\"deviceScaleFactor\": 2, \"userAgent\": \"Mozilla/5.0 (Linux; Android " - "4.0; LG-E975 Build/IMM76L) AppleWebKit/535.19 (KHTML, like Gecko) " - "Chrome/18.0.1025.166 Mobile Safari/535.19\", \"touch\": true, \"mobile\": " - "true},{\"title\": \"LG Optimus LTE, Optimus 4X HD\", \"width\": 424, " - "\"height\": 753, \"deviceScaleFactor\": 1.7, \"userAgent\": \"Mozilla/5.0 " - "(Linux; U; Android 2.3; en-us; LG-P930 Build/GRJ90) AppleWebKit/533.1 " - "(KHTML, like Gecko) Version/4.0 Mobile Safari/533.1\", \"touch\": true, " - "\"mobile\": true},{\"title\": \"LG Optimus One\", \"width\": 213, " - "\"height\": 320, \"deviceScaleFactor\": 1.5, \"userAgent\": \"Mozilla/5.0 " - "(Linux; U; Android 2.2.1; en-us; LG-MS690 Build/FRG83) AppleWebKit/533.1 " - "(KHTML, like Gecko) Version/4.0 Mobile Safari/533.1\", \"touch\": true, " - "\"mobile\": true},{\"title\": \"Motorola Defy, Droid, Droid X, " - "Milestone\", \"width\": 320, \"height\": 569, \"deviceScaleFactor\": 1.5, " - "\"userAgent\": \"Mozilla/5.0 (Linux; U; Android 2.0; en-us; Milestone " - "Build/ SHOLS_U2_01.03.1) AppleWebKit/530.17 (KHTML, like Gecko) " - "Version/4.0 Mobile Safari/530.17\", \"touch\": true, \"mobile\": " - "true},{\"title\": \"Motorola Droid 3, Droid 4, Droid Razr, Atrix 4G, " - "Atrix 2\", \"width\": 540, \"height\": 960, \"deviceScaleFactor\": 1, " - "\"userAgent\": \"Mozilla/5.0 (Linux; U; Android 2.2; en-us; Droid " - "Build/FRG22D) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile " - "Safari/533.1\", \"touch\": true, \"mobile\": true},{\"title\": \"Motorola " - "Droid Razr HD\", \"width\": 720, \"height\": 1280, \"deviceScaleFactor\": " - "1, \"userAgent\": \"Mozilla/5.0 (Linux; U; Android 2.3; en-us; DROID RAZR " - "4G Build/6.5.1-73_DHD-11_M1-29) AppleWebKit/533.1 (KHTML, like Gecko) " - "Version/4.0 Mobile Safari/533.1\", \"touch\": true, \"mobile\": " - "true},{\"title\": \"Nokia C5, C6, C7, N97, N8, X7\", \"width\": 360, " - "\"height\": 640, \"deviceScaleFactor\": 1, \"userAgent\": " - "\"NokiaN97/21.1.107 (SymbianOS/9.4; Series60/5.0 Mozilla/5.0; " - "Profile/MIDP-2.1 Configuration/CLDC-1.1) AppleWebkit/525 (KHTML, like " - "Gecko) BrowserNG/7.1.4\", \"touch\": true, \"mobile\": true},{\"title\": " - "\"Nokia Lumia 7X0, Lumia 8XX, Lumia 900, N800, N810, N900\", \"width\": " - "320, \"height\": 533, \"deviceScaleFactor\": 1.5, \"userAgent\": " - "\"Mozilla/5.0 (compatible; MSIE 10.0; Windows Phone 8.0; Trident/6.0; " - "IEMobile/10.0; ARM; Touch; NOKIA; Lumia 820)\", \"touch\": true, " - "\"mobile\": true},{\"title\": \"Samsung Galaxy Note 3\", \"width\": 360, " - "\"height\": 640, \"deviceScaleFactor\": 3, \"userAgent\": \"Mozilla/5.0 " - "(Linux; U; Android 4.3; en-us; SM-N900T Build/JSS15J) AppleWebKit/534.30 " - "(KHTML, like Gecko) Version/4.0 Mobile Safari/534.30\", \"touch\": true, " - "\"mobile\": true},{\"title\": \"Samsung Galaxy Note II\", \"width\": 360, " - "\"height\": 640, \"deviceScaleFactor\": 2, \"userAgent\": \"Mozilla/5.0 " - "(Linux; U; Android 4.1; en-us; GT-N7100 Build/JRO03C) AppleWebKit/534.30 " - "(KHTML, like Gecko) Version/4.0 Mobile Safari/534.30\", \"touch\": true, " - "\"mobile\": true},{\"title\": \"Samsung Galaxy Note\", \"width\": 400, " - "\"height\": 640, \"deviceScaleFactor\": 2, \"userAgent\": \"Mozilla/5.0 " - "(Linux; U; Android 2.3; en-us; SAMSUNG-SGH-I717 Build/GINGERBREAD) " - "AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1\", " - "\"touch\": true, \"mobile\": true},{\"title\": \"Samsung Galaxy S III, " - "Galaxy Nexus\", \"width\": 360, \"height\": 640, \"deviceScaleFactor\": " - "2, \"userAgent\": \"Mozilla/5.0 (Linux; U; Android 4.0; en-us; GT-I9300 " - "Build/IMM76D) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile " - "Safari/534.30\", \"touch\": true, \"mobile\": true},{\"title\": \"Samsung " - "Galaxy S, S II, W\", \"width\": 320, \"height\": 533, " - "\"deviceScaleFactor\": 1.5, \"userAgent\": \"Mozilla/5.0 (Linux; U; " - "Android 2.1; en-us; GT-I9000 Build/ECLAIR) AppleWebKit/525.10+ (KHTML, " - "like Gecko) Version/3.0.4 Mobile Safari/523.12.2\", \"touch\": true, " - "\"mobile\": true},{\"title\": \"Samsung Galaxy S4\", \"width\": 360, " - "\"height\": 640, \"deviceScaleFactor\": 3, \"userAgent\": \"Mozilla/5.0 " - "(Linux; Android 4.2.2; GT-I9505 Build/JDQ39) AppleWebKit/537.36 (KHTML, " - "like Gecko) Chrome/31.0.1650.59 Mobile Safari/537.36\", \"touch\": true, " - "\"mobile\": true},{\"title\": \"Sony Xperia S, Ion\", \"width\": 360, " - "\"height\": 640, \"deviceScaleFactor\": 2, \"userAgent\": \"Mozilla/5.0 " - "(Linux; U; Android 4.0; en-us; LT28at Build/6.1.C.1.111) " + "4.4.4; en-us; Nexus 5 Build/JOP40D) AppleWebKit/537.36 (KHTML, like " + "Gecko) Chrome/42.0.2307.2 Mobile Safari/537.36\", \"touch\": true, " + "\"mobile\": true},{\"title\": \"LG Optimus L70\", \"width\": 384, " + "\"height\": 640, \"deviceScaleFactor\": 1.25, \"userAgent\": " + "\"Mozilla/5.0 (Linux; U; Android 4.4.2; en-us; LGMS323 " + "Build/KOT49I.MS32310c) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 " + "Chrome/30.0.1599.103 Mobile Safari/537.36\", \"touch\": true, \"mobile\": " + "true},{\"title\": \"Nokia N9\", \"width\": 360, \"height\": 640, " + "\"deviceScaleFactor\": 1, \"userAgent\": \"Mozilla/5.0 (MeeGo; NokiaN9) " + "AppleWebKit/534.13 (KHTML, like Gecko) NokiaBrowser/8.5.0 Mobile " + "Safari/534.13\", \"touch\": true, \"mobile\": true},{\"title\": \"Nokia " + "Lumia 520\", \"width\": 320, \"height\": 533, \"deviceScaleFactor\": 1.4, " + "\"userAgent\": \"Mozilla/5.0 (compatible; MSIE 10.0; Windows Phone 8.0; " + "Trident/6.0; IEMobile/10.0; ARM; Touch; NOKIA; Lumia 520)\", \"touch\": " + "true, \"mobile\": true},{\"title\": \"Samsung Galaxy S III\", \"width\": " + "360, \"height\": 640, \"deviceScaleFactor\": 2, \"userAgent\": " + "\"Mozilla/5.0 (Linux; U; Android 4.0; en-us; GT-I9300 Build/IMM76D) " "AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile " - "Safari/534.30\", \"touch\": true, \"mobile\": true},{\"title\": \"Sony " - "Xperia Sola, U\", \"width\": 480, \"height\": 854, \"deviceScaleFactor\": " - "1, \"userAgent\": \"Mozilla/5.0 (Linux; U; Android 2.3; en-us; " - "SonyEricssonST25i Build/6.0.B.1.564) AppleWebKit/533.1 (KHTML, like " - "Gecko) Version/4.0 Mobile Safari/533.1\", \"touch\": true, \"mobile\": " - "true},{\"title\": \"Sony Xperia Z, Z1\", \"width\": 360, \"height\": 640, " - "\"deviceScaleFactor\": 3, \"userAgent\": \"Mozilla/5.0 (Linux; U; Android " - "4.2; en-us; SonyC6903 Build/14.1.G.1.518) AppleWebKit/534.30 (KHTML, like " - "Gecko) Version/4.0 Mobile Safari/534.30\", \"touch\": true, \"mobile\": " - "true},{\"title\": \"Amazon Kindle Fire HDX 7″\", \"width\": 1920, " - "\"height\": 1200, \"deviceScaleFactor\": 2, \"userAgent\": \"Mozilla/5.0 " - "(Linux; U; en-us; KFTHWI Build/JDQ39) AppleWebKit/535.19 (KHTML, like " - "Gecko) Silk/3.13 Safari/535.19 Silk-Accelerated=true\", \"touch\": true, " - "\"mobile\": true},{\"title\": \"Amazon Kindle Fire HDX 8.9″\", \"width\": " - "2560, \"height\": 1600, \"deviceScaleFactor\": 2, \"userAgent\": " - "\"Mozilla/5.0 (Linux; U; en-us; KFAPWI Build/JDQ39) AppleWebKit/535.19 " - "(KHTML, like Gecko) Silk/3.13 Safari/535.19 Silk-Accelerated=true\", " - "\"touch\": true, \"mobile\": true},{\"title\": \"Amazon Kindle Fire " - "(First Generation)\", \"width\": 1024, \"height\": 600, " - "\"deviceScaleFactor\": 1, \"userAgent\": \"Mozilla/5.0 (Macintosh; U; " - "Intel Mac OS X 10_6_3; en-us; Silk/1.0.141.16-Gen4_11004310) " - "AppleWebkit/533.16 (KHTML, like Gecko) Version/5.0 Safari/533.16 " - "Silk-Accelerated=true\", \"touch\": true, \"mobile\": true},{\"title\": " - "\"Apple iPad 1 / 2 / iPad Mini\", \"width\": 1024, \"height\": 768, " + "Safari/534.30\", \"touch\": true, \"mobile\": true},{\"title\": \"Samsung " + "Galaxy S4\", \"width\": 360, \"height\": 640, \"deviceScaleFactor\": 3, " + "\"userAgent\": \"Mozilla/5.0 (Linux; Android 4.4.2; GT-I9505 Build/JDQ39) " + "AppleWebKit/537.36 (KHTML, like Gecko) Version/1.5 Chrome/28.0.1500.94 " + "Mobile Safari/537.36\", \"touch\": true, \"mobile\": true},{\"title\": " + "\"Amazon Kindle Fire HDX\", \"width\": 2560, \"height\": 1600, " + "\"deviceScaleFactor\": 2, \"userAgent\": \"Mozilla/5.0 (Linux; U; en-us; " + "KFAPWI Build/JDQ39) AppleWebKit/535.19 (KHTML, like Gecko) Silk/3.13 " + "Safari/535.19 Silk-Accelerated=true\", \"touch\": true, \"mobile\": " + "true},{\"title\": \"Apple iPad Mini\", \"width\": 1024, \"height\": 768, " "\"deviceScaleFactor\": 1, \"userAgent\": \"Mozilla/5.0 (iPad; CPU OS " "4_3_5 like Mac OS X; en-us) AppleWebKit/533.17.9 (KHTML, like Gecko) " "Version/5.0.2 Mobile/8L1 Safari/6533.18.5\", \"touch\": true, \"mobile\": " - "true},{\"title\": \"Apple iPad 3 / 4\", \"width\": 1024, \"height\": 768, " + "true},{\"title\": \"Apple iPad\", \"width\": 1024, \"height\": 768, " "\"deviceScaleFactor\": 2, \"userAgent\": \"Mozilla/5.0 (iPad; CPU OS 7_0 " "like Mac OS X) AppleWebKit/537.51.1 (KHTML, like Gecko) Version/7.0 " "Mobile/11A465 Safari/9537.53\", \"touch\": true, \"mobile\": " @@ -172,25 +79,24 @@ "true},{\"title\": \"Google Nexus 10\", \"width\": 1280, \"height\": 800, " "\"deviceScaleFactor\": 2, \"userAgent\": \"Mozilla/5.0 (Linux; Android " "4.3; Nexus 10 Build/JSS15Q) AppleWebKit/537.36 (KHTML, like Gecko) " - "Chrome/29.0.1547.72 Safari/537.36\", \"touch\": true, \"mobile\": " - "true},{\"title\": \"Google Nexus 7 2\", \"width\": 960, \"height\": 600, " + "Chrome/42.0.2307.2 Mobile Safari/537.36\", \"touch\": true, \"mobile\": " + "true},{\"title\": \"Google Nexus 7\", \"width\": 960, \"height\": 600, " "\"deviceScaleFactor\": 2, \"userAgent\": \"Mozilla/5.0 (Linux; Android " "4.3; Nexus 7 Build/JSS15Q) AppleWebKit/537.36 (KHTML, like Gecko) " - "Chrome/29.0.1547.72 Safari/537.36\", \"touch\": true, \"mobile\": " - "true},{\"title\": \"Google Nexus 7\", \"width\": 966, \"height\": 604, " - "\"deviceScaleFactor\": 1.325, \"userAgent\": \"Mozilla/5.0 (Linux; " - "Android 4.3; Nexus 7 Build/JSS15Q) AppleWebKit/537.36 (KHTML, like Gecko) " - "Chrome/29.0.1547.72 Safari/537.36\", \"touch\": true, \"mobile\": " - "true},{\"title\": \"Motorola Xoom, Xyboard\", \"width\": 1280, " - "\"height\": 800, \"deviceScaleFactor\": 1, \"userAgent\": \"Mozilla/5.0 " - "(Linux; U; Android 3.0; en-us; Xoom Build/HRI39) AppleWebKit/525.10 " - "(KHTML, like Gecko) Version/3.0.4 Mobile Safari/523.12.2\", \"touch\": " - "true, \"mobile\": true},{\"title\": \"Samsung Galaxy Tab 7.7, 8.9, " - "10.1\", \"width\": 1280, \"height\": 800, \"deviceScaleFactor\": 1, " - "\"userAgent\": \"Mozilla/5.0 (Linux; U; Android 2.2; en-us; SCH-I800 " - "Build/FROYO) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile " - "Safari/533.1\", \"touch\": true, \"mobile\": true},{\"title\": \"Samsung " - "Galaxy Tab\", \"width\": 1024, \"height\": 600, \"deviceScaleFactor\": 1, " - "\"userAgent\": \"Mozilla/5.0 (Linux; U; Android 2.2; en-us; SCH-I800 " - "Build/FROYO) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile " - "Safari/533.1\", \"touch\": true, \"mobile\": true}]"; + "Chrome/42.0.2307.2 Mobile Safari/537.36\", \"touch\": true, \"mobile\": " + "true},{\"title\": \"Samsung Galaxy Note 3\", \"width\": 360, \"height\": " + "640, \"deviceScaleFactor\": 3, \"userAgent\": \"Mozilla/5.0 (Linux; U; " + "Android 4.3; en-us; SM-N900T Build/JSS15J) AppleWebKit/534.30 (KHTML, " + "like Gecko) Version/4.0 Mobile Safari/534.30\", \"touch\": true, " + "\"mobile\": true},{\"title\": \"Samsung Galaxy Note II\", \"width\": 360, " + "\"height\": 640, \"deviceScaleFactor\": 2, \"userAgent\": \"Mozilla/5.0 " + "(Linux; U; Android 4.1; en-us; GT-N7100 Build/JRO03C) AppleWebKit/534.30 " + "(KHTML, like Gecko) Version/4.0 Mobile Safari/534.30\", \"touch\": true, " + "\"mobile\": true},{\"title\": \"Laptop with touch\", \"width\": 1280, " + "\"height\": 950, \"deviceScaleFactor\": 1, \"userAgent\": \"\", " + "\"touch\": true, \"mobile\": false},{\"title\": \"Laptop with HiDPI " + "screen\", \"width\": 1440, \"height\": 900, \"deviceScaleFactor\": 2, " + "\"userAgent\": \"\", \"touch\": false, \"mobile\": false},{\"title\": " + "\"Laptop with MDPI screen\", \"width\": 1280, \"height\": 800, " + "\"deviceScaleFactor\": 1, \"userAgent\": \"\", \"touch\": false, " + "\"mobile\": false}]";
diff --git a/chrome/test/chromedriver/chrome/mobile_device_list.h b/chrome/test/chromedriver/chrome/mobile_device_list.h index e2bb874..e85e3a4 100644 --- a/chrome/test/chromedriver/chrome/mobile_device_list.h +++ b/chrome/test/chromedriver/chrome/mobile_device_list.h
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// This file was generated at (2015-01-22 12:13:16.600114) by running: +// This file was generated at (2015-03-30 16:08:35.102812) by running: // chrome/test/chromedriver/embed_mobile_devices_in_cpp.py --directory // chrome/test/chromedriver/chrome/ // third_party/WebKit/Source/devtools/front_end/toolbox/OverridesUI.js
diff --git a/chrome/test/chromedriver/chrome/mobile_emulation_override_manager.cc b/chrome/test/chromedriver/chrome/mobile_emulation_override_manager.cc index ca3c531..34d6d10 100644 --- a/chrome/test/chromedriver/chrome/mobile_emulation_override_manager.cc +++ b/chrome/test/chromedriver/chrome/mobile_emulation_override_manager.cc
@@ -56,9 +56,12 @@ if (status.IsError()) return status; - // Always emulate touch. - base::DictionaryValue emulate_touch_params; - emulate_touch_params.SetBoolean("enabled", true); - return client_->SendCommand( - "Page.setTouchEmulationEnabled", emulate_touch_params); + if (overridden_device_metrics_->touch) { + base::DictionaryValue emulate_touch_params; + emulate_touch_params.SetBoolean("enabled", true); + status = client_->SendCommand( + "Page.setTouchEmulationEnabled", emulate_touch_params); + } + + return Status(kOk); }
diff --git a/chrome/test/chromedriver/chrome/mobile_emulation_override_manager_unittest.cc b/chrome/test/chromedriver/chrome/mobile_emulation_override_manager_unittest.cc index c8cf20c..e980205 100644 --- a/chrome/test/chromedriver/chrome/mobile_emulation_override_manager_unittest.cc +++ b/chrome/test/chromedriver/chrome/mobile_emulation_override_manager_unittest.cc
@@ -36,9 +36,9 @@ } // namespace -TEST(MobileEmulationOverrideManager, SendsCommandOnConnect) { +TEST(MobileEmulationOverrideManager, SendsCommandWithTouchOnConnect) { RecorderDevToolsClient client; - DeviceMetrics device_metrics(1, 2, 3.0); + DeviceMetrics device_metrics(1, 2, 3.0, true, true); MobileEmulationOverrideManager manager(&client, &device_metrics); ASSERT_EQ(0u, client.commands_.size()); ASSERT_EQ(kOk, manager.OnConnected(&client).code()); @@ -50,9 +50,23 @@ AssertDeviceMetricsCommand(client.commands_[2], device_metrics)); } +TEST(MobileEmulationOverrideManager, SendsCommandWithoutTouchOnConnect) { + RecorderDevToolsClient client; + DeviceMetrics device_metrics(1, 2, 3.0, false, true); + MobileEmulationOverrideManager manager(&client, &device_metrics); + ASSERT_EQ(0u, client.commands_.size()); + ASSERT_EQ(kOk, manager.OnConnected(&client).code()); + + ASSERT_EQ(1u, client.commands_.size()); + ASSERT_EQ(kOk, manager.OnConnected(&client).code()); + ASSERT_EQ(2u, client.commands_.size()); + ASSERT_NO_FATAL_FAILURE( + AssertDeviceMetricsCommand(client.commands_[1], device_metrics)); +} + TEST(MobileEmulationOverrideManager, SendsCommandOnNavigation) { RecorderDevToolsClient client; - DeviceMetrics device_metrics(1, 2, 3.0); + DeviceMetrics device_metrics(1, 2, 3.0, true, true); MobileEmulationOverrideManager manager(&client, &device_metrics); base::DictionaryValue main_frame_params; ASSERT_EQ(kOk,
diff --git a/chrome/test/chromedriver/embed_mobile_devices_in_cpp.py b/chrome/test/chromedriver/embed_mobile_devices_in_cpp.py index 327012d..2aed44b 100755 --- a/chrome/test/chromedriver/embed_mobile_devices_in_cpp.py +++ b/chrome/test/chromedriver/embed_mobile_devices_in_cpp.py
@@ -50,6 +50,9 @@ if 'WebInspector.OverridesUI._tablets = [' in line: devices += ',' inside_list = True + if 'WebInspector.OverridesUI._notebooks = [' in line: + devices += ',' + inside_list = True else: if line.strip() == '];': inside_list = False
diff --git a/chrome/test/chromedriver/test/run_py_tests.py b/chrome/test/chromedriver/test/run_py_tests.py index 6a417f3..ef0506d3 100755 --- a/chrome/test/chromedriver/test/run_py_tests.py +++ b/chrome/test/chromedriver/test/run_py_tests.py
@@ -1231,9 +1231,9 @@ self.assertEqual(640, driver.ExecuteScript('return window.screen.height')) body_tag = driver.FindElement('tag name', 'body') self.assertEqual( - 'Mozilla/5.0 (Linux; Android 4.2.1; en-us; Nexus 5 Build/JOP40D) AppleW' - 'ebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.166 Mobile Safari/53' - '5.19', + 'Mozilla/5.0 (Linux; Android 4.4.4; en-us; Nexus 5 Build/JOP40D) ' + 'AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2307.2 Mobile ' + 'Safari/537.36', body_tag.GetText()) def testSendKeysToElement(self):
diff --git a/chrome/test/data/android/banners/native_app_manifest.json b/chrome/test/data/android/banners/native_app_manifest.json new file mode 100644 index 0000000..f17b499e --- /dev/null +++ b/chrome/test/data/android/banners/native_app_manifest.json
@@ -0,0 +1,7 @@ +{ + "prefer_related_applications": true, + "related_applications": [{ + "platform": "play", + "id": "test.package" + }] +}
diff --git a/chrome/test/data/android/banners/native_app_test.html b/chrome/test/data/android/banners/native_app_test.html index 3575a3a0..28f4f6c 100644 --- a/chrome/test/data/android/banners/native_app_test.html +++ b/chrome/test/data/android/banners/native_app_test.html
@@ -1,7 +1,7 @@ <html> <head> <title>AppBannerManager test page</title> - <meta name="google-play-id" content="test.package" /> + <link rel="manifest" href="native_app_manifest.json" /> </head> <body> Promoting the "test.package" package.
diff --git a/chrome/test/data/banners/play_app_manifest.json b/chrome/test/data/banners/play_app_manifest.json new file mode 100644 index 0000000..170015f --- /dev/null +++ b/chrome/test/data/banners/play_app_manifest.json
@@ -0,0 +1,7 @@ +{ + "prefer_related_applications": true, + "related_applications": [{ + "platform": "play", + "id": "123456" + }] +}
diff --git a/chrome/test/data/banners/play_app_test_page.html b/chrome/test/data/banners/play_app_test_page.html new file mode 100644 index 0000000..459d19c --- /dev/null +++ b/chrome/test/data/banners/play_app_test_page.html
@@ -0,0 +1,9 @@ +<html> + <head> + <title>Web app banner test page</title> + <link rel="manifest" href="play_app_manifest.json" /> + </head> + <body onload="initialize()"> + Do-nothing page with a service worker. + </body> +</html>
diff --git a/chrome/test/data/extensions/api_test/bluetooth_private/discovery_filter/manifest.json b/chrome/test/data/extensions/api_test/bluetooth_private/discovery_filter/manifest.json new file mode 100644 index 0000000..5b6dfb94 --- /dev/null +++ b/chrome/test/data/extensions/api_test/bluetooth_private/discovery_filter/manifest.json
@@ -0,0 +1,15 @@ +{ + // extension id: jofgjdphhceggjecimellaapdjjadibj + "name": "Test Setting Bluetooth Discovery Filter", + "description": "Tests chrome.bluetoothPrivate.setDiscoveryFilter function.", + "version": "1.0", + "manifest_version": 2, + "key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2nI64+TVbJNYUfte1hEwrWpjgiH3ucfKZ12NC6IT/Pm2pQdjR/3alrdW+rCkYbs0KmfUymb0GOE7fwZ0Gs+EqfxoKmwJaaZiv86GXEkPJctDvjqrJRUrKvM6aXZEkTQeaFLQVY9NDk3grSZzvC365l3c4zRN3A2i8KMWzB9HRQzKnN49zjgcTTu5DERYTzbJZBd0m9Ln1b3x3UVkVgoTUq7DexGMcOq1KYz0VHrFRo/LN1yJvECFmBb2pdl40g4UHq3UqrWDDInZZJ3sr01EePxYYwimMFsGnvH6sz8wHC09rXZ+w1YFYjsQ3P/3Bih1q/NdZ0aop3MEOCbHb4gipQIDAQAB", + "app": { + "background": { + "scripts": ["test.js"] + } + }, + "bluetooth": {}, + "permissions": [ "bluetoothPrivate" ] +}
diff --git a/chrome/test/data/extensions/api_test/bluetooth_private/discovery_filter/test.js b/chrome/test/data/extensions/api_test/bluetooth_private/discovery_filter/test.js new file mode 100644 index 0000000..cb30cc6 --- /dev/null +++ b/chrome/test/data/extensions/api_test/bluetooth_private/discovery_filter/test.js
@@ -0,0 +1,29 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +function testSetDiscoveryFilter() { + // Pre-set discovery filter + chrome.bluetoothPrivate.setDiscoveryFilter( + { + uuids: ["cafe", "0000bebe-0000-1000-8000-00805f9b34fb"], + transport: "le", + pathloss: 50 + }, + function() { + chrome.test.assertNoLastError(); + // Start discovery with pre-set filter. + chrome.bluetooth.startDiscovery(function(){ + chrome.test.assertNoLastError(); + + // Change filter (clear) during scan. + chrome.bluetoothPrivate.setDiscoveryFilter({}, function() { + chrome.test.assertNoLastError(); + // Success. + chrome.test.succeed(); + }); + }); + }); +} + +chrome.test.runTests([ testSetDiscoveryFilter ]);
diff --git a/chrome/test/data/extensions/api_test/mime_handler_view/embedded.html b/chrome/test/data/extensions/api_test/mime_handler_view/embedded.html deleted file mode 100644 index b5648d6..0000000 --- a/chrome/test/data/extensions/api_test/mime_handler_view/embedded.html +++ /dev/null
@@ -1,6 +0,0 @@ -<html> -<body> -This is the embedded MIME type handler. -</body> -<script src="embedded.js"></script> -</html>
diff --git a/chrome/test/data/extensions/api_test/mime_handler_view/index.html b/chrome/test/data/extensions/api_test/mime_handler_view/index.html new file mode 100644 index 0000000..ea5d99fa --- /dev/null +++ b/chrome/test/data/extensions/api_test/mime_handler_view/index.html
@@ -0,0 +1,6 @@ +<html> +<body> +This is the MIME type handler. +</body> +<script src="index.js"></script> +</html>
diff --git a/chrome/test/data/extensions/api_test/mime_handler_view/embedded.js b/chrome/test/data/extensions/api_test/mime_handler_view/index.js similarity index 74% rename from chrome/test/data/extensions/api_test/mime_handler_view/embedded.js rename to chrome/test/data/extensions/api_test/mime_handler_view/index.js index 73415213..1fbe620 100644 --- a/chrome/test/data/extensions/api_test/mime_handler_view/embedded.js +++ b/chrome/test/data/extensions/api_test/mime_handler_view/index.js
@@ -38,15 +38,24 @@ chrome.test.assertEq('content to read\n', response.data); } +function expectSuccessfulReadLong(response) { + chrome.test.assertEq(200, response.status); + chrome.test.assertTrue(response.data.indexOf('content to read\n') === 0); +} + function checkStreamDetails(name, embedded) { - chrome.test.assertTrue(streamDetails.originalUrl.indexOf(name) != -1); - chrome.test.assertEq('text/csv', streamDetails.mimeType); - chrome.test.assertTrue(streamDetails.tabId != -1); + checkStreamDetailsNoFile(); chrome.test.assertEq(embedded, streamDetails.embedded); + chrome.test.assertTrue(streamDetails.originalUrl.indexOf(name) != -1); chrome.test.assertEq('text/csv', streamDetails.responseHeaders['Content-Type']); } +function checkStreamDetailsNoFile() { + chrome.test.assertEq('text/csv', streamDetails.mimeType); + chrome.test.assertTrue(streamDetails.tabId != -1); +} + var tests = [ function testBasic() { checkStreamDetails('testBasic.csv', false); @@ -86,6 +95,15 @@ }); }, + function testNonAsciiHeaders() { + checkStreamDetails('testNonAsciiHeaders.csv', false); + chrome.test.assertEq(undefined, + streamDetails.responseHeaders['Content-Disposition']); + chrome.test.assertEq(undefined, + streamDetails.responseHeaders['ü']); + chrome.test.succeed(); + }, + function testPostMessage() { var expectedMessages = ['hey', 100, 25.0]; var messagesReceived = 0; @@ -107,6 +125,21 @@ handleMessage(queuedMessages.shift()); } + }, + + function testDataUrl() { + // TODO(raymes): have separate checks for embedded/unembedded data URLs. + checkStreamDetailsNoFile(); + fetchUrl(streamDetails.streamUrl) + .then(expectSuccessfulRead) + .then(chrome.test.succeed); + }, + + function testDataUrlLong() { + checkStreamDetailsNoFile(); + fetchUrl(streamDetails.streamUrl) + .then(expectSuccessfulReadLong) + .then(chrome.test.succeed); } ]; @@ -128,4 +161,14 @@ window.removeEventListener('message', queueMessage); chrome.test.runTests([testsByName[test]]); } + + // Run the test for data URLs. + if (streamInfo.originalUrl.indexOf("data:") === 0) { + window.removeEventListener('message', queueMessage); + // Long data URLs get truncated. + if (streamInfo.originalUrl == "data:") + chrome.test.runTests([testsByName['testDataUrlLong']]); + else + chrome.test.runTests([testsByName['testDataUrl']]); + } });
diff --git a/chrome/test/data/extensions/api_test/mime_handler_view/manifest.json b/chrome/test/data/extensions/api_test/mime_handler_view/manifest.json index 3519fd4..bc5c1025 100644 --- a/chrome/test/data/extensions/api_test/mime_handler_view/manifest.json +++ b/chrome/test/data/extensions/api_test/mime_handler_view/manifest.json
@@ -6,5 +6,5 @@ "mime_types": [ "text/csv" ], - "mime_types_handler": "embedded.html" + "mime_types_handler": "index.html" }
diff --git a/chrome/test/data/extensions/api_test/mime_handler_view/testNonAsciiHeaders.csv b/chrome/test/data/extensions/api_test/mime_handler_view/testNonAsciiHeaders.csv new file mode 100644 index 0000000..1e30f4ad --- /dev/null +++ b/chrome/test/data/extensions/api_test/mime_handler_view/testNonAsciiHeaders.csv
@@ -0,0 +1 @@ +content to read
diff --git a/chrome/test/data/extensions/api_test/mime_handler_view/testNonAsciiHeaders.csv.mock-http-headers b/chrome/test/data/extensions/api_test/mime_handler_view/testNonAsciiHeaders.csv.mock-http-headers new file mode 100644 index 0000000..e23b9e4 --- /dev/null +++ b/chrome/test/data/extensions/api_test/mime_handler_view/testNonAsciiHeaders.csv.mock-http-headers
@@ -0,0 +1,4 @@ +HTTP/1.0 200 OK +Content-Type: text/csv +Content-Disposition: inline; filename=ü +ü: value
diff --git a/chrome/test/data/extensions/api_test/mime_handler_view/test_embedded_data_url_embed.html b/chrome/test/data/extensions/api_test/mime_handler_view/test_embedded_data_url_embed.html new file mode 100644 index 0000000..046377e5 --- /dev/null +++ b/chrome/test/data/extensions/api_test/mime_handler_view/test_embedded_data_url_embed.html
@@ -0,0 +1,5 @@ +<html> +<body> +<embed id="plugin" width="200" height="200" src="data:text/csv;base64,Y29udGVudCB0byByZWFkCg==" type="text/csv"></object> +</body> +</html>
diff --git a/chrome/test/data/extensions/api_test/mime_handler_view/test_embedded_data_url_long.html b/chrome/test/data/extensions/api_test/mime_handler_view/test_embedded_data_url_long.html new file mode 100644 index 0000000..c2d88951 --- /dev/null +++ b/chrome/test/data/extensions/api_test/mime_handler_view/test_embedded_data_url_long.html
@@ -0,0 +1,5 @@ +<html> +<body> +<object id="plugin" width="200" height="200" data="data:text/csv;base64,Y29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCmNvbnRlbnQgdG8gcmVhZApjb250ZW50IHRvIHJlYWQKY29udGVudCB0byByZWFkCg==" type="text/csv"></object> +</body> +</html>
diff --git a/chrome/test/data/extensions/api_test/mime_handler_view/test_embedded_data_url_object.html b/chrome/test/data/extensions/api_test/mime_handler_view/test_embedded_data_url_object.html new file mode 100644 index 0000000..6d17d01b3 --- /dev/null +++ b/chrome/test/data/extensions/api_test/mime_handler_view/test_embedded_data_url_object.html
@@ -0,0 +1,5 @@ +<html> +<body> +<object id="plugin" width="200" height="200" data="data:text/csv;base64,Y29udGVudCB0byByZWFkCg==" type="text/csv"></object> +</body> +</html>
diff --git a/chrome/test/data/nacl/nacl_test_data.gyp b/chrome/test/data/nacl/nacl_test_data.gyp index 0372000..78d3aa6 100644 --- a/chrome/test/data/nacl/nacl_test_data.gyp +++ b/chrome/test/data/nacl/nacl_test_data.gyp
@@ -126,9 +126,6 @@ 'simple_cc.js', ], }, - 'dependencies': [ - '<(DEPTH)/native_client/tools.gyp:prep_toolchain', - ], }, { 'target_name': 'sysconf_nprocessors_onln_test', @@ -166,9 +163,6 @@ '../../../../ppapi/native_client/tests/ppapi_test_lib/testable_callback.cc', ] }, - 'dependencies': [ - '<(DEPTH)/native_client/tools.gyp:prep_toolchain', - ], }, { 'target_name': 'ppapi_progress_events', @@ -193,7 +187,6 @@ ], }, 'dependencies': [ - '<(DEPTH)/native_client/tools.gyp:prep_toolchain', '<(DEPTH)/native_client/src/shared/platform/platform.gyp:platform_lib', '<(DEPTH)/native_client/src/shared/gio/gio.gyp:gio_lib', '<(DEPTH)/ppapi/native_client/native_client.gyp:ppapi_lib', @@ -220,7 +213,6 @@ ], }, 'dependencies': [ - '<(DEPTH)/native_client/tools.gyp:prep_toolchain', '<(DEPTH)/native_client/src/shared/platform/platform.gyp:platform_lib', '<(DEPTH)/native_client/src/shared/gio/gio.gyp:gio_lib', '<(DEPTH)/ppapi/native_client/native_client.gyp:ppapi_lib', @@ -245,7 +237,6 @@ ], }, 'dependencies': [ - '<(DEPTH)/native_client/tools.gyp:prep_toolchain', '<(DEPTH)/native_client/src/shared/platform/platform.gyp:platform_lib', '<(DEPTH)/native_client/src/shared/gio/gio.gyp:gio_lib', '<(DEPTH)/ppapi/native_client/native_client.gyp:ppapi_lib', @@ -270,7 +261,6 @@ ], }, 'dependencies': [ - '<(DEPTH)/native_client/tools.gyp:prep_toolchain', '<(DEPTH)/native_client/src/shared/platform/platform.gyp:platform_lib', '<(DEPTH)/native_client/src/shared/gio/gio.gyp:gio_lib', '<(DEPTH)/ppapi/native_client/native_client.gyp:ppapi_lib', @@ -295,7 +285,6 @@ ], }, 'dependencies': [ - '<(DEPTH)/native_client/tools.gyp:prep_toolchain', '<(DEPTH)/native_client/src/shared/platform/platform.gyp:platform_lib', '<(DEPTH)/native_client/src/shared/gio/gio.gyp:gio_lib', '<(DEPTH)/ppapi/native_client/native_client.gyp:ppapi_lib', @@ -320,7 +309,6 @@ ], }, 'dependencies': [ - '<(DEPTH)/native_client/tools.gyp:prep_toolchain', '<(DEPTH)/native_client/src/shared/platform/platform.gyp:platform_lib', '<(DEPTH)/native_client/src/shared/gio/gio.gyp:gio_lib', '<(DEPTH)/ppapi/native_client/native_client.gyp:ppapi_lib', @@ -345,7 +333,6 @@ ], }, 'dependencies': [ - '<(DEPTH)/native_client/tools.gyp:prep_toolchain', '<(DEPTH)/native_client/src/shared/platform/platform.gyp:platform_lib', '<(DEPTH)/native_client/src/shared/gio/gio.gyp:gio_lib', '<(DEPTH)/ppapi/native_client/native_client.gyp:ppapi_lib', @@ -371,7 +358,6 @@ ], }, 'dependencies': [ - '<(DEPTH)/native_client/tools.gyp:prep_toolchain', '<(DEPTH)/native_client/src/shared/platform/platform.gyp:platform_lib', '<(DEPTH)/native_client/src/shared/gio/gio.gyp:gio_lib', '<(DEPTH)/ppapi/native_client/native_client.gyp:ppapi_lib', @@ -399,7 +385,6 @@ ], }, 'dependencies': [ - '<(DEPTH)/native_client/tools.gyp:prep_toolchain', '<(DEPTH)/native_client/src/shared/platform/platform.gyp:platform_lib', '<(DEPTH)/native_client/src/shared/gio/gio.gyp:gio_lib', '<(DEPTH)/ppapi/native_client/native_client.gyp:ppapi_lib', @@ -427,7 +412,6 @@ ], }, 'dependencies': [ - '<(DEPTH)/native_client/tools.gyp:prep_toolchain', '<(DEPTH)/native_client/src/shared/platform/platform.gyp:platform_lib', '<(DEPTH)/native_client/src/shared/gio/gio.gyp:gio_lib', '<(DEPTH)/ppapi/native_client/native_client.gyp:ppapi_lib', @@ -455,7 +439,6 @@ ], }, 'dependencies': [ - '<(DEPTH)/native_client/tools.gyp:prep_toolchain', '<(DEPTH)/native_client/src/shared/platform/platform.gyp:platform_lib', '<(DEPTH)/native_client/src/shared/gio/gio.gyp:gio_lib', '<(DEPTH)/ppapi/native_client/native_client.gyp:ppapi_lib', @@ -483,7 +466,6 @@ ], }, 'dependencies': [ - '<(DEPTH)/native_client/tools.gyp:prep_toolchain', '<(DEPTH)/native_client/src/shared/platform/platform.gyp:platform_lib', '<(DEPTH)/native_client/src/shared/gio/gio.gyp:gio_lib', '<(DEPTH)/ppapi/native_client/native_client.gyp:ppapi_lib', @@ -525,7 +507,6 @@ ], }, 'dependencies': [ - '<(DEPTH)/native_client/tools.gyp:prep_toolchain', '<(DEPTH)/ppapi/ppapi_nacl.gyp:ppapi_cpp_lib', '<(DEPTH)/ppapi/native_client/native_client.gyp:ppapi_lib', '<(DEPTH)/ppapi/native_client/src/untrusted/pnacl_irt_shim/pnacl_irt_shim.gyp:aot', @@ -586,7 +567,6 @@ ], }, 'dependencies': [ - '<(DEPTH)/native_client/tools.gyp:prep_toolchain', '<(DEPTH)/native_client/src/shared/platform/platform.gyp:platform_lib', '<(DEPTH)/native_client/src/shared/gio/gio.gyp:gio_lib', '<(DEPTH)/native_client/src/untrusted/nacl/nacl.gyp:nacl_exception_lib', @@ -641,7 +621,6 @@ ], }, 'dependencies': [ - '<(DEPTH)/native_client/tools.gyp:prep_toolchain', '<(DEPTH)/ppapi/native_client/native_client.gyp:ppapi_lib', '<(DEPTH)/native_client/src/shared/platform/platform.gyp:platform_lib', '<(DEPTH)/native_client/src/shared/gio/gio.gyp:gio_lib', @@ -666,9 +645,6 @@ 'pnacl_debug_url/pnacl_no_debug.nmf', ], }, - 'dependencies': [ - '<(DEPTH)/native_client/tools.gyp:prep_toolchain', - ] }, { 'target_name': 'pnacl_error_handling_test', @@ -686,9 +662,6 @@ 'pnacl_error_handling/pnacl_illformed_manifest.nmf', ], }, - 'dependencies': [ - '<(DEPTH)/native_client/tools.gyp:prep_toolchain', - ] }, { 'target_name': 'pnacl_mime_type_test', @@ -721,9 +694,6 @@ 'pnacl_nmf_options/pnacl_o_large.nmf', ], }, - 'dependencies': [ - '<(DEPTH)/native_client/tools.gyp:prep_toolchain', - ] }, { 'target_name': 'pnacl_dyncode_syscall_disabled_test', @@ -754,7 +724,6 @@ '<(DEPTH)/native_client/src/shared/gio/gio.gyp:gio_lib', '<(DEPTH)/native_client/src/shared/platform/platform.gyp:platform_lib', '<(DEPTH)/native_client/src/untrusted/nacl/nacl.gyp:nacl_dyncode_private_lib', - '<(DEPTH)/native_client/tools.gyp:prep_toolchain', '<(DEPTH)/ppapi/native_client/native_client.gyp:ppapi_lib', '<(DEPTH)/ppapi/ppapi_nacl.gyp:ppapi_cpp_lib', 'ppapi_test_lib', @@ -789,7 +758,6 @@ '<(DEPTH)/native_client/src/shared/gio/gio.gyp:gio_lib', '<(DEPTH)/native_client/src/shared/platform/platform.gyp:platform_lib', '<(DEPTH)/native_client/src/untrusted/nacl/nacl.gyp:nacl_exception_private_lib', - '<(DEPTH)/native_client/tools.gyp:prep_toolchain', '<(DEPTH)/ppapi/native_client/native_client.gyp:ppapi_lib', '<(DEPTH)/ppapi/ppapi_nacl.gyp:ppapi_cpp_lib', 'ppapi_test_lib', @@ -819,7 +787,6 @@ ], }, 'dependencies': [ - '<(DEPTH)/native_client/tools.gyp:prep_toolchain', '<(DEPTH)/native_client/src/shared/platform/platform.gyp:platform_lib', '<(DEPTH)/native_client/src/shared/gio/gio.gyp:gio_lib', '<(DEPTH)/ppapi/native_client/native_client.gyp:ppapi_lib', @@ -850,7 +817,6 @@ ], }, 'dependencies': [ - '<(DEPTH)/native_client/tools.gyp:prep_toolchain', '<(DEPTH)/native_client/src/shared/platform/platform.gyp:platform_lib', '<(DEPTH)/native_client/src/shared/gio/gio.gyp:gio_lib', '<(DEPTH)/ppapi/native_client/native_client.gyp:ppapi_lib', @@ -882,7 +848,6 @@ ], }, 'dependencies': [ - '<(DEPTH)/native_client/tools.gyp:prep_toolchain', '<(DEPTH)/native_client/src/shared/platform/platform.gyp:platform_lib', '<(DEPTH)/native_client/src/shared/gio/gio.gyp:gio_lib', '<(DEPTH)/ppapi/native_client/native_client.gyp:ppapi_lib', @@ -920,9 +885,6 @@ }], ], }, - 'dependencies': [ - '<(DEPTH)/native_client/tools.gyp:prep_toolchain', - ], }, ], }],
diff --git a/chrome/test/data/navigation_interception/navigation_from_image_onload.html b/chrome/test/data/navigation_interception/navigation_from_image_onload.html new file mode 100644 index 0000000..6d053f5 --- /dev/null +++ b/chrome/test/data/navigation_interception/navigation_from_image_onload.html
@@ -0,0 +1,42 @@ +<!DOCTYPE html> +<html> +<!-- + +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. + +--> +<head> + <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"> + <style> + #first { + position: absolute; + width: 100px; + height: 100px; + top: 0px; + left: 0px; + background-color: green; + -webkit-transform: translate3d(0, 0, 0); + } + </style> + <script> + function pingAndOpenHello(e) { + var img = new Image(); + img.onload = function () { + window.location = 'hello.html'; + } + img.src = '../image_search/valid.png'; + + e.preventDefault(); + }; + + function setup() { + first.ontouchstart = pingAndOpenHello; + }; + </script> +</head> +<body onload='setup();'> + <div id='first'></div> +</body> +</html>
diff --git a/chrome/test/data/pdf/navigator_test.js b/chrome/test/data/pdf/navigator_test.js index 21c6bb9..149fe2f 100644 --- a/chrome/test/data/pdf/navigator_test.js +++ b/chrome/test/data/pdf/navigator_test.js
@@ -32,7 +32,7 @@ var mockSizer = new MockSizer(); var mockCallback = new MockViewportChangedCallback(); var viewport = new Viewport(mockWindow, mockSizer, mockCallback.callback, - function() {}, function() {}, 0, 0); + function() {}, function() {}, 0, 1); var paramsParser = new OpenPDFParamsParser(function(name) { if (name == 'US')
diff --git a/chrome/test/data/pdf/viewport_test.js b/chrome/test/data/pdf/viewport_test.js index fc232a93..a513de93 100644 --- a/chrome/test/data/pdf/viewport_test.js +++ b/chrome/test/data/pdf/viewport_test.js
@@ -6,7 +6,7 @@ function testDocumentNeedsScrollbars() { var viewport = new Viewport(new MockWindow(100, 100), new MockSizer(), function() {}, - function() {}, function() {}, 10, 0); + function() {}, function() {}, 10, 1); var scrollbars; viewport.setDocumentDimensions(new MockDocumentDimensions(90, 90)); @@ -61,7 +61,7 @@ var mockWindow = new MockWindow(100, 100, mockSizer); var mockCallback = new MockViewportChangedCallback(); var viewport = new Viewport(mockWindow, mockSizer, mockCallback.callback, - function() {}, function() {}, 0, 0); + function() {}, function() {}, 0, 1); // Test setting the zoom without the document dimensions set. The sizer // shouldn't change size. @@ -136,7 +136,7 @@ function testGetMostVisiblePage() { var mockWindow = new MockWindow(100, 100); var viewport = new Viewport(mockWindow, new MockSizer(), function() {}, - function() {}, function() {}, 0, 0); + function() {}, function() {}, 0, 1); var documentDimensions = new MockDocumentDimensions(100, 100); documentDimensions.addPage(50, 100); @@ -186,7 +186,7 @@ var mockSizer = new MockSizer(); var mockCallback = new MockViewportChangedCallback(); var viewport = new Viewport(mockWindow, mockSizer, mockCallback.callback, - function() {}, function() {}, 0, 0); + function() {}, function() {}, 0, 1); var documentDimensions = new MockDocumentDimensions(); // Test with a document width which matches the window width. @@ -245,7 +245,7 @@ // fit to width, which will cause the page height to span outside of the // viewport, triggering 15px scrollbars to be shown. viewport = new Viewport(mockWindow, mockSizer, mockCallback.callback, - function() {}, function() {}, 15, 0); + function() {}, function() {}, 15, 1); documentDimensions.reset(); documentDimensions.addPage(50, 100); viewport.setDocumentDimensions(documentDimensions); @@ -264,7 +264,7 @@ var mockSizer = new MockSizer(); var mockCallback = new MockViewportChangedCallback(); var viewport = new Viewport(mockWindow, mockSizer, mockCallback.callback, - function() {}, function() {}, 0, 0); + function() {}, function() {}, 0, 1); var documentDimensions = new MockDocumentDimensions(); // Test with a page size which matches the window size. @@ -373,7 +373,7 @@ var mockSizer = new MockSizer(); var mockCallback = new MockViewportChangedCallback(); var viewport = new Viewport(mockWindow, mockSizer, mockCallback.callback, - function() {}, function() {}, 0, 0); + function() {}, function() {}, 0, 1); var documentDimensions = new MockDocumentDimensions(); documentDimensions.addPage(100, 100); @@ -414,7 +414,7 @@ var mockSizer = new MockSizer(); var mockCallback = new MockViewportChangedCallback(); var viewport = new Viewport(mockWindow, mockSizer, mockCallback.callback, - function() {}, function() {}, 0, 0); + function() {}, function() {}, 0, 1); var documentDimensions = new MockDocumentDimensions(); documentDimensions.addPage(100, 100); documentDimensions.addPage(200, 200); @@ -469,10 +469,28 @@ chrome.test.assertEq(1, viewport.zoom); }; viewport = new Viewport(mockWindow, mockSizer, function() {}, - beforeZoom, afterZoom, 0, 0); + beforeZoom, afterZoom, 0, 1); viewport.setZoom(0.5); chrome.test.succeed(); - } + }, + + function testInitialSetDocumentDimensionsZoomConstrained() { + var viewport = + new Viewport(new MockWindow(100, 100), new MockSizer(), function() {}, + function() {}, function() {}, 0, 1.2); + viewport.setDocumentDimensions(new MockDocumentDimensions(50, 50)); + chrome.test.assertEq(1.2, viewport.zoom); + chrome.test.succeed(); + }, + + function testInitialSetDocumentDimensionsZoomUnconstrained() { + var viewport = new Viewport( + new MockWindow(100, 100), + new MockSizer(), function() {}, function() {}, function() {}, 0, 3); + viewport.setDocumentDimensions(new MockDocumentDimensions(50, 50)); + chrome.test.assertEq(2, viewport.zoom); + chrome.test.succeed(); + }, ]; chrome.test.runTests(tests);
diff --git a/chrome/test/data/pdf/zoom_manager_test.js b/chrome/test/data/pdf/zoom_manager_test.js index 6ce0a3b1b..f02010c6 100644 --- a/chrome/test/data/pdf/zoom_manager_test.js +++ b/chrome/test/data/pdf/zoom_manager_test.js
@@ -54,7 +54,8 @@ let viewport = new MockViewport(); let browserZoomSetter = new MockBrowserZoomSetter(); let zoomManager = new ZoomManager( - viewport, browserZoomSetter.setBrowserZoom.bind(browserZoomSetter)); + viewport, browserZoomSetter.setBrowserZoom.bind(browserZoomSetter), + 1); viewport.zoom = 2; zoomManager.onPdfZoomChange(); chrome.test.assertEq(2, browserZoomSetter.zoom); @@ -62,21 +63,9 @@ chrome.test.succeed(); }, - function testZoomChangedBeforeConstruction() { - let viewport = new MockViewport(); - viewport.zoom = 2; - let browserZoomSetter = new MockBrowserZoomSetter(); - let zoomManager = new ZoomManager( - viewport, browserZoomSetter.setBrowserZoom.bind(browserZoomSetter)); - chrome.test.assertEq(2, browserZoomSetter.zoom); - chrome.test.assertTrue(browserZoomSetter.started); - chrome.test.succeed(); - }, - function testBrowserZoomChange() { let viewport = new MockViewport(); - let zoomManager = new ZoomManager(viewport, Promise.resolve.bind(Promise), - chrome.test.fail); + let zoomManager = new ZoomManager(viewport, chrome.test.fail, 1); zoomManager.onBrowserZoomChange(3); chrome.test.assertEq(1, viewport.zooms.length); chrome.test.assertEq(3, viewport.zooms[0]); @@ -88,8 +77,9 @@ let viewport = new MockViewport(); let browserZoomSetter = new MockBrowserZoomSetter(); let zoomManager = new ZoomManager( - viewport, browserZoomSetter.setBrowserZoom.bind(browserZoomSetter)); - viewport.zoom = 1.0001; + viewport, browserZoomSetter.setBrowserZoom.bind(browserZoomSetter), + 2); + viewport.zoom = 2.0001; zoomManager.onPdfZoomChange(); chrome.test.assertEq(1, browserZoomSetter.zoom); chrome.test.assertFalse(browserZoomSetter.started); @@ -98,8 +88,7 @@ function testSmallBrowserZoomChange() { let viewport = new MockViewport(); - let zoomManager = new ZoomManager(viewport, Promise.resolve.bind(Promise), - chrome.test.fail); + let zoomManager = new ZoomManager(viewport, chrome.test.fail, 1); zoomManager.onBrowserZoomChange(0.999); chrome.test.assertEq(0, viewport.zooms.length); chrome.test.assertEq(1, viewport.zoom); @@ -110,7 +99,8 @@ let viewport = new MockViewport(); let browserZoomSetter = new MockBrowserZoomSetter(); let zoomManager = new ZoomManager( - viewport, browserZoomSetter.setBrowserZoom.bind(browserZoomSetter)); + viewport, browserZoomSetter.setBrowserZoom.bind(browserZoomSetter), + 1); viewport.zoom = 2; zoomManager.onPdfZoomChange(); viewport.zoom = 3; @@ -127,8 +117,7 @@ function testMultipleBrowserZoomChanges() { let viewport = new MockViewport(); - let zoomManager = new ZoomManager(viewport, Promise.resolve.bind(Promise), - chrome.test.fail); + let zoomManager = new ZoomManager(viewport, chrome.test.fail, 1); zoomManager.onBrowserZoomChange(2); zoomManager.onBrowserZoomChange(3); chrome.test.assertEq(2, viewport.zooms.length);
diff --git a/chrome/test/data/policy/policy_test_cases.json b/chrome/test/data/policy/policy_test_cases.json index 7106453..cb38eb4 100644 --- a/chrome/test/data/policy/policy_test_cases.json +++ b/chrome/test/data/policy/policy_test_cases.json
@@ -2300,8 +2300,8 @@ "official_only": true, "test_policy": { "DeviceMetricsReportingEnabled": true }, "pref_mappings": [ - { "pref": "spellcheck.use_spelling_service", - "indicator_test_setup_js": "Preferences.getInstance().addEventListener('alternate_error_pages.enabled', function(event) { Preferences.prefsChangedCallback(['cros.metrics.reportingEnabled', {value: event.value.value, controlledBy: event.value.controlledBy, disabled: event.value.disabled}]); });", + { "pref": "cros.metrics.reportingEnabled", + "indicator_test_setup_js": "var controllingPref = 'spellcheck.use_spelling_service'; var testedPref = 'cros.metrics.reportingEnabled'; Preferences.prefsChangedCallback([testedPref, Preferences.getInstance().registeredPreferences_[controllingPref].orig]); Preferences.getInstance().addEventListener(controllingPref, function(event) {Preferences.prefsChangedCallback([testedPref, {value: event.value.value, controlledBy: event.value.controlledBy, disabled: event.value.disabled}]);});", "indicator_tests": [ { "policy": { "SpellCheckServiceEnabled": true } } ]
diff --git a/chrome/test/data/push_messaging/push_test.js b/chrome/test/data/push_messaging/push_test.js index be99f62..19118ad 100644 --- a/chrome/test/data/push_messaging/push_test.js +++ b/chrome/test/data/push_messaging/push_test.js
@@ -5,7 +5,7 @@ 'use strict'; var resultQueue = new ResultQueue(); -var pushRegistration = null; +var pushSubscription = null; // Sends data back to the test. This must be in response to an earlier // request, but it's ok to respond asynchronously. The request blocks until @@ -57,7 +57,7 @@ }; // Notification permission has been coalesced with Push permission. After -// this is granted, Push API registration can succeed. +// this is granted, Push API subscription can succeed. function requestNotificationPermission() { Notification.requestPermission(function(permission) { sendResultToTest('permission status - ' + permission); @@ -92,15 +92,13 @@ } } -function registerPush() { +function subscribePush() { navigator.serviceWorker.ready.then(function(swRegistration) { - var registerMethodName = - swRegistration.pushManager.register ? 'register' : 'subscribe'; - return swRegistration.pushManager[registerMethodName]() - .then(function(registration) { - pushRegistration = registration; - sendResultToTest(registration.endpoint + ' - ' + - (registration.registrationId || registration.subscriptionId)); + return swRegistration.pushManager.subscribe() + .then(function(subscription) { + pushSubscription = subscription; + sendResultToTest( + subscription.endpoint + ' - ' + subscription.subscriptionId); }); }).catch(sendErrorToTest); } @@ -122,27 +120,25 @@ } } -function unregister() { - if (!pushRegistration) { - sendResultToTest('unregister error: no registration'); +function unsubscribePush() { + if (!pushSubscription) { + sendResultToTest('unsubscribe error: no subscription'); return; } - var unregisterMethodName = - pushRegistration.unregister ? 'unregister' : 'unsubscribe'; - pushRegistration[unregisterMethodName]().then(function(result) { - sendResultToTest('unregister result: ' + result); + pushSubscription.unsubscribe().then(function(result) { + sendResultToTest('unsubscribe result: ' + result); }, function(error) { - sendResultToTest('unregister error: ' + error.name + ': ' + error.message); + sendResultToTest('unsubscribe error: ' + error.name + ': ' + error.message); }); } -function hasRegistration() { +function hasSubscription() { navigator.serviceWorker.ready.then(function(swRegistration) { return swRegistration.pushManager.getSubscription(); }).then(function(subscription) { - sendResultToTest(subscription ? 'true - registered' - : 'false - not registered'); + sendResultToTest(subscription ? 'true - subscribed' + : 'false - not subscribed'); }).catch(sendErrorToTest); }
diff --git a/chrome/utility/BUILD.gn b/chrome/utility/BUILD.gn index c469307..dbf408f 100644 --- a/chrome/utility/BUILD.gn +++ b/chrome/utility/BUILD.gn
@@ -83,21 +83,15 @@ } } - if (use_openssl) { - if (!is_win && !is_mac && !is_android) { - sources -= [ "importer/nss_decryptor.cc" ] - } - } else { # !use_openssl - if (!is_win && !is_mac) { - sources += [ - "importer/nss_decryptor_system_nss.cc", - "importer/nss_decryptor_system_nss.h", - ] - deps += [ - "//crypto", - "//crypto:platform", - ] - } + if (use_nss_certs) { + sources += [ + "importer/nss_decryptor_system_nss.cc", + "importer/nss_decryptor_system_nss.h", + ] + deps += [ + "//crypto", + "//crypto:platform", + ] } if (!enable_print_preview) {
diff --git a/chrome/utility/chrome_content_utility_client.cc b/chrome/utility/chrome_content_utility_client.cc index 1c79cdf4..111ac99c 100644 --- a/chrome/utility/chrome_content_utility_client.cc +++ b/chrome/utility/chrome_content_utility_client.cc
@@ -207,9 +207,13 @@ // static SkBitmap ChromeContentUtilityClient::DecodeImage( const std::vector<unsigned char>& encoded_data, bool shrink_to_fit) { - SkBitmap decoded_image = content::DecodeImage(&encoded_data[0], - gfx::Size(), - encoded_data.size()); + SkBitmap decoded_image; + if (encoded_data.empty()) + return decoded_image; + + decoded_image = content::DecodeImage(&encoded_data[0], + gfx::Size(), + encoded_data.size()); int64_t struct_size = sizeof(ChromeUtilityHostMsg_DecodeImage_Succeeded); int64_t image_size = decoded_image.computeSize64(); @@ -298,10 +302,9 @@ Send(new ChromeUtilityHostMsg_DetectSeccompSupport_ResultPrctl( supports_prctl)); - bool supports_syscall = sandbox::SandboxBPF::SupportsSeccompSandbox( - sandbox::SandboxBPF::SeccompLevel::MULTI_THREADED); - Send(new ChromeUtilityHostMsg_DetectSeccompSupport_ResultSyscall( - supports_syscall)); + // Probing for the seccomp syscall can provoke kernel panics in certain LGE + // devices. For now, this data will not be collected. In the future, this + // should detect SeccompLevel::MULTI_THREADED. http://crbug.com/478478 ReleaseProcessIfNeeded(); }
diff --git a/chrome/utility/importer/ie_importer_win.cc b/chrome/utility/importer/ie_importer_win.cc index 8da54a3..e1f5e4fb 100644 --- a/chrome/utility/importer/ie_importer_win.cc +++ b/chrome/utility/importer/ie_importer_win.cc
@@ -634,7 +634,7 @@ // import a password from IE whose scheme is https, we give it the benefit // of the doubt and DON'T auto-fill it unless the form appears under // valid TLS conditions. - form.ssl_valid = url.SchemeUsesTLS(); + form.ssl_valid = url.SchemeIsCryptographic(); // Goes through the list to find out the username field // of the web page.
diff --git a/chrome/utility/importer/nss_decryptor.cc b/chrome/utility/importer/nss_decryptor.cc index 5c54638..01bdb0a 100644 --- a/chrome/utility/importer/nss_decryptor.cc +++ b/chrome/utility/importer/nss_decryptor.cc
@@ -202,7 +202,7 @@ form.signon_realm = form.origin.GetOrigin().spec(); if (!realm.empty()) form.signon_realm += realm; - form.ssl_valid = form.origin.SchemeUsesTLS(); + form.ssl_valid = form.origin.SchemeIsCryptographic(); ++begin; // There may be multiple username/password pairs for this site. @@ -296,7 +296,7 @@ // digest_auth entry, so let's assume basic_auth. form.scheme = autofill::PasswordForm::SCHEME_BASIC; } - form.ssl_valid = form.origin.SchemeUsesTLS(); + form.ssl_valid = form.origin.SchemeIsCryptographic(); // The user name, password and action. form.username_element = s2.ColumnString16(3); form.username_value = Decrypt(s2.ColumnString(5));
diff --git a/chrome/utility/importer/nss_decryptor.h b/chrome/utility/importer/nss_decryptor.h index 5c36112..d53e640b 100644 --- a/chrome/utility/importer/nss_decryptor.h +++ b/chrome/utility/importer/nss_decryptor.h
@@ -11,15 +11,10 @@ #include "chrome/utility/importer/nss_decryptor_mac.h" #elif defined(OS_WIN) #include "chrome/utility/importer/nss_decryptor_win.h" -#elif defined(USE_OPENSSL) -// TODO(joth): It should be an error to include this file with USE_OPENSSL -// defined. (Unless there is a way to do nss decrypt with OpenSSL). Ideally -// we remove the importers that depend on NSS when doing USE_OPENSSL builds, but -// that is going to take some non-trivial refactoring so in the meantime we're -// just falling back to a no-op implementation. -#include "chrome/utility/importer/nss_decryptor_null.h" #elif defined(USE_NSS_CERTS) #include "chrome/utility/importer/nss_decryptor_system_nss.h" +#else +#error NSSDecryptor not implemented. #endif #endif // CHROME_UTILITY_IMPORTER_NSS_DECRYPTOR_H_
diff --git a/chrome/utility/importer/nss_decryptor_null.h b/chrome/utility/importer/nss_decryptor_null.h deleted file mode 100644 index e99334ba..0000000 --- a/chrome/utility/importer/nss_decryptor_null.h +++ /dev/null
@@ -1,44 +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 CHROME_UTILITY_IMPORTER_NSS_DECRYPTOR_NULL_H_ -#define CHROME_UTILITY_IMPORTER_NSS_DECRYPTOR_NULL_H_ - -#include <string> -#include <vector> - -#include "base/basictypes.h" -#include "base/strings/string16.h" - -namespace autofill { -struct PasswordForm; -} - -namespace base { -class FilePath; -} - -// A NULL wrapper for Firefox NSS decrypt component, for use in builds where -// we do not have the NSS library. -class NSSDecryptor { - public: - NSSDecryptor() {} - bool Init(const base::FilePath& dll_path, const base::FilePath& db_path) { - return false; - } - base::string16 Decrypt(const std::string& crypt) const { - return base::string16(); - } - void ParseSignons(const std::string& content, - std::vector<autofill::PasswordForm>* forms) {} - bool ReadAndParseSignons(const base::FilePath& sqlite_file, - std::vector<autofill::PasswordForm>* forms) { - return false; - } - - private: - DISALLOW_COPY_AND_ASSIGN(NSSDecryptor); -}; - -#endif // CHROME_UTILITY_IMPORTER_NSS_DECRYPTOR_NULL_H_
diff --git a/chrome_elf/blacklist/blacklist.cc b/chrome_elf/blacklist/blacklist.cc index 31219cc9..6aef1cc 100644 --- a/chrome_elf/blacklist/blacklist.cc +++ b/chrome_elf/blacklist/blacklist.cc
@@ -41,6 +41,7 @@ L"crdli.dll", // Linkury Inc. L"crdli64.dll", // Linkury Inc. L"datamngr.dll", // Unknown (suspected adware). + L"explorerex.dll", // Unknown (suspected adware). L"hk.dll", // Unknown (keystroke logger). L"libapi2hook.dll", // V-Bates. L"libinject.dll", // V-Bates.
diff --git a/chrome_elf/chrome_elf_util_unittest.cc b/chrome_elf/chrome_elf_util_unittest.cc index 90b04ed1..ad83801 100644 --- a/chrome_elf/chrome_elf_util_unittest.cc +++ b/chrome_elf/chrome_elf_util_unittest.cc
@@ -57,7 +57,7 @@ const char*, const char*> > { protected: - virtual void SetUp() override { + void SetUp() override { override_manager_.OverrideRegistry(HKEY_LOCAL_MACHINE); override_manager_.OverrideRegistry(HKEY_CURRENT_USER); const char* app;
diff --git a/chrome_elf/create_file/chrome_create_file_unittest.cc b/chrome_elf/create_file/chrome_create_file_unittest.cc index cd66d47..8b0331fe 100644 --- a/chrome_elf/create_file/chrome_create_file_unittest.cc +++ b/chrome_elf/create_file/chrome_create_file_unittest.cc
@@ -76,7 +76,7 @@ path); } - virtual void SetUp() override { + void SetUp() override { original_thread_ = base::PlatformThread::CurrentId(); InitCache(); PlatformTest::SetUp();
diff --git a/chrome_elf/ntdll_cache_unittest.cc b/chrome_elf/ntdll_cache_unittest.cc index a96df615..d3f3555a 100644 --- a/chrome_elf/ntdll_cache_unittest.cc +++ b/chrome_elf/ntdll_cache_unittest.cc
@@ -13,7 +13,7 @@ class NTDLLCacheTest : public testing::Test { protected: - virtual void SetUp() override { + void SetUp() override { InitCache(); }
diff --git a/chromecast/BUILD.gn b/chromecast/BUILD.gn new file mode 100644 index 0000000..6169cf8 --- /dev/null +++ b/chromecast/BUILD.gn
@@ -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. + +import("//chromecast/chromecast.gni") + +config("config") { + defines = [] + + if (use_playready) { + defines += [ "PLAYREADY_CDM_AVAILABLE" ] + } +} + +component("chromecast") { + deps = [ + "//chromecast/base", + "//chromecast/base/metrics", + "//chromecast/media", + ] +}
diff --git a/chromecast/DEPS b/chromecast/DEPS index d278340..eae26b0 100644 --- a/chromecast/DEPS +++ b/chromecast/DEPS
@@ -5,6 +5,7 @@ # sub-directory within chromecast/. "-chromecast", "+chromecast/base", + "+chromecast/public", # Other Chromecast-wide dependencies. "+content/public/common",
diff --git a/chromecast/base/BUILD.gn b/chromecast/base/BUILD.gn new file mode 100644 index 0000000..b105a71 --- /dev/null +++ b/chromecast/base/BUILD.gn
@@ -0,0 +1,12 @@ +# 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("base") { + sources = [ + "cast_paths.cc", + "cast_paths.h", + ] + + configs += [ "//chromecast:config" ] +}
diff --git a/chromecast/base/cast_sys_info_dummy.cc b/chromecast/base/cast_sys_info_dummy.cc new file mode 100644 index 0000000..92c39a9 --- /dev/null +++ b/chromecast/base/cast_sys_info_dummy.cc
@@ -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. + +#include "chromecast/base/cast_sys_info_dummy.h" + +namespace chromecast { + +CastSysInfoDummy::CastSysInfoDummy() { +} + +CastSysInfoDummy::~CastSysInfoDummy() { +} + +CastSysInfo::BuildType CastSysInfoDummy::GetBuildType() { + return BUILD_ENG; +} + +std::string CastSysInfoDummy::GetSystemReleaseChannel() { + return ""; +} + +std::string CastSysInfoDummy::GetSerialNumber() { + return "dummy.serial.number"; +} + +std::string CastSysInfoDummy::GetProductName() { + return ""; +} + +std::string CastSysInfoDummy::GetDeviceModel() { + return ""; +} + +std::string CastSysInfoDummy::GetBoardName() { + return ""; +} + +std::string CastSysInfoDummy::GetBoardRevision() { + return ""; +} + +std::string CastSysInfoDummy::GetManufacturer() { + return ""; +} + +std::string CastSysInfoDummy::GetSystemBuildNumber() { + return ""; +} + +std::string CastSysInfoDummy::GetFactoryCountry() { + return "US"; +} + +std::string CastSysInfoDummy::GetFactoryLocale(std::string* second_locale) { + return "en-US"; +} + +std::string CastSysInfoDummy::GetWifiInterface() { + return ""; +} + +std::string CastSysInfoDummy::GetApInterface() { + return ""; +} + +} // namespace chromecast
diff --git a/chromecast/base/cast_sys_info_dummy.h b/chromecast/base/cast_sys_info_dummy.h new file mode 100644 index 0000000..0b804a2 --- /dev/null +++ b/chromecast/base/cast_sys_info_dummy.h
@@ -0,0 +1,39 @@ +// 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 CHROMECAST_BASE_CAST_SYS_INFO_DUMMY_H_ +#define CHROMECAST_BASE_CAST_SYS_INFO_DUMMY_H_ + +#include "base/macros.h" +#include "chromecast/public/cast_sys_info.h" + +namespace chromecast { + +class CastSysInfoDummy : public CastSysInfo { + public: + CastSysInfoDummy(); + ~CastSysInfoDummy() override; + + // CastSysInfo implementation: + BuildType GetBuildType() override; + std::string GetSystemReleaseChannel() override; + std::string GetSerialNumber() override; + std::string GetProductName() override; + std::string GetDeviceModel() override; + std::string GetBoardName() override; + std::string GetBoardRevision() override; + std::string GetManufacturer() override; + std::string GetSystemBuildNumber() override; + std::string GetFactoryCountry() override; + std::string GetFactoryLocale(std::string* second_locale) override; + std::string GetWifiInterface() override; + std::string GetApInterface() override; + + private: + DISALLOW_COPY_AND_ASSIGN(CastSysInfoDummy); +}; + +} // namespace chromecast + +#endif // CHROMECAST_BASE_CAST_SYS_INFO_DUMMY_H_
diff --git a/chromecast/base/cast_sys_info_util.h b/chromecast/base/cast_sys_info_util.h new file mode 100644 index 0000000..05ca069 --- /dev/null +++ b/chromecast/base/cast_sys_info_util.h
@@ -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. + +#ifndef CHROMECAST_BASE_CAST_SYS_INFO_UTIL_H_ +#define CHROMECAST_BASE_CAST_SYS_INFO_UTIL_H_ + +#include <string> +#include <vector> + +#include "base/memory/scoped_ptr.h" + +namespace chromecast { + +class CastSysInfo; + +scoped_ptr<CastSysInfo> CreateSysInfo(); + +} // namespace chromecast + +#endif // CHROMECAST_BASE_CAST_SYS_INFO_UTIL_H_
diff --git a/chromecast/base/cast_sys_info_util_simple.cc b/chromecast/base/cast_sys_info_util_simple.cc new file mode 100644 index 0000000..68ab9dff --- /dev/null +++ b/chromecast/base/cast_sys_info_util_simple.cc
@@ -0,0 +1,16 @@ +// 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 "chromecast/base/cast_sys_info_util.h" + +#include "chromecast/base/cast_sys_info_dummy.h" + +namespace chromecast { + +// static +scoped_ptr<CastSysInfo> CreateSysInfo() { + return make_scoped_ptr(new CastSysInfoDummy()); +} + +} // namespace chromecast
diff --git a/chromecast/base/metrics/BUILD.gn b/chromecast/base/metrics/BUILD.gn new file mode 100644 index 0000000..7ed81a5 --- /dev/null +++ b/chromecast/base/metrics/BUILD.gn
@@ -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. + +source_set("metrics") { + sources = [ + "cast_histograms.h", + "cast_metrics_helper.cc", + "cast_metrics_helper.h", + "grouped_histogram.cc", + "grouped_histogram.h", + ] + + deps = [ + "//chromecast/base", + ] + + configs += [ "//chromecast:config" ] +} + +source_set("test_support") { + testonly = true + + sources = [ + "cast_metrics_test_helper.cc", + "cast_metrics_test_helper.h", + ] + + deps = [ + ":metrics", + "//chromecast/base", + ] + + configs += [ "//chromecast:config" ] +}
diff --git a/chromecast/base/serializers.cc b/chromecast/base/serializers.cc new file mode 100644 index 0000000..366a5d2 --- /dev/null +++ b/chromecast/base/serializers.cc
@@ -0,0 +1,33 @@ +// 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 "chromecast/base/serializers.h" + +#include "base/json/json_string_value_serializer.h" +#include "base/logging.h" + +namespace chromecast { + +scoped_ptr<base::Value> DeserializeFromJson(const std::string& text) { + JSONStringValueDeserializer deserializer(text); + + int error_code; + std::string error_msg; + scoped_ptr<base::Value> value( + deserializer.Deserialize(&error_code, &error_msg)); + DLOG_IF(ERROR, !value) << "JSON error " << error_code << ":" << error_msg; + + // Value will hold the nullptr in case of an error. + return value.Pass(); +} + +scoped_ptr<std::string> SerializeToJson(const base::Value& value) { + scoped_ptr<std::string> json_str(new std::string()); + JSONStringValueSerializer serializer(json_str.get()); + if (!serializer.Serialize(value)) + json_str.reset(nullptr); + return json_str.Pass(); +} + +} // namespace chromecast
diff --git a/chromecast/base/serializers.h b/chromecast/base/serializers.h new file mode 100644 index 0000000..df278fdbc0 --- /dev/null +++ b/chromecast/base/serializers.h
@@ -0,0 +1,29 @@ +// 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 CHROMECAST_BASE_SERIALIZERS_H_ +#define CHROMECAST_BASE_SERIALIZERS_H_ + +#include <string> + +#include "base/memory/scoped_ptr.h" + +namespace base { +class Value; +} + +namespace chromecast { + +// Helper function which deserializes JSON |text| into a base::Value. If |text| +// is empty, is not valid JSON, or if some other deserialization error occurs, +// the return value will hold the NULL pointer. +scoped_ptr<base::Value> DeserializeFromJson(const std::string& text); + +// Helper function which serializes |value| into a JSON string. If a +// serialization error occurs,the return value will hold the NULL pointer. +scoped_ptr<std::string> SerializeToJson(const base::Value& value); + +} // namespace chromecast + +#endif // CHROMECAST_BASE_SERIALIZERS_H_
diff --git a/chromecast/base/serializers_unittest.cc b/chromecast/base/serializers_unittest.cc new file mode 100644 index 0000000..e82d0dc --- /dev/null +++ b/chromecast/base/serializers_unittest.cc
@@ -0,0 +1,76 @@ +// 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/values.h" +#include "chromecast/base/serializers.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace chromecast { +namespace { +const char kEmptyJsonString[] = "{}"; +const char kProperJsonString[] = + "{\n" + " \"compound\": {\n" + " \"a\": 1,\n" + " \"b\": 2\n" + " },\n" + " \"some_String\": \"1337\",\n" + " \"some_int\": 42,\n" + " \"the_list\": [ \"val1\", \"val2\" ]\n" + "}\n"; +const char kPoorlyFormedJsonString[] = "{\"key\":"; +const char kTestKey[] = "test_key"; +const char kTestValue[] = "test_value"; + +} // namespace + +TEST(DeserializeFromJson, EmptyString) { + std::string str; + scoped_ptr<base::Value> value = DeserializeFromJson(str); + EXPECT_EQ(nullptr, value.get()); +} + +TEST(DeserializeFromJson, EmptyJsonObject) { + std::string str = kEmptyJsonString; + scoped_ptr<base::Value> value = DeserializeFromJson(str); + EXPECT_NE(nullptr, value.get()); +} + +TEST(DeserializeFromJson, ProperJsonObject) { + std::string str = kProperJsonString; + scoped_ptr<base::Value> value = DeserializeFromJson(str); + EXPECT_NE(nullptr, value.get()); +} + +TEST(DeserializeFromJson, PoorlyFormedJsonObject) { + std::string str = kPoorlyFormedJsonString; + scoped_ptr<base::Value> value = DeserializeFromJson(str); + EXPECT_EQ(nullptr, value.get()); +} + +TEST(SerializeToJson, BadValue) { + base::BinaryValue value(scoped_ptr<char[]>(new char[12]), 12); + scoped_ptr<std::string> str = SerializeToJson(value); + EXPECT_EQ(nullptr, str.get()); +} + +TEST(SerializeToJson, EmptyValue) { + base::DictionaryValue value; + scoped_ptr<std::string> str = SerializeToJson(value); + ASSERT_NE(nullptr, str.get()); + EXPECT_EQ(kEmptyJsonString, *str); +} + +TEST(SerializeToJson, PopulatedValue) { + base::DictionaryValue orig_value; + orig_value.SetString(kTestKey, kTestValue); + scoped_ptr<std::string> str = SerializeToJson(orig_value); + ASSERT_NE(nullptr, str.get()); + + scoped_ptr<base::Value> new_value = DeserializeFromJson(*str); + ASSERT_NE(nullptr, new_value.get()); + EXPECT_TRUE(new_value->Equals(&orig_value)); +} + +} // namespace chromecast
diff --git a/chromecast/browser/DEPS b/chromecast/browser/DEPS index e91498e..53066424 100644 --- a/chromecast/browser/DEPS +++ b/chromecast/browser/DEPS
@@ -4,6 +4,7 @@ "+cc/base/switches.h", "+chromecast", "+components/crash", + "+components/devtools_discovery", "+components/devtools_http_handler", "+components/network_hints/browser", "+content/public/browser",
diff --git a/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastWindowAndroid.java b/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastWindowAndroid.java index 9ad13c2..ed85dcf 100644 --- a/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastWindowAndroid.java +++ b/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastWindowAndroid.java
@@ -127,7 +127,7 @@ private void initFromNativeWebContents(WebContents webContents, int renderProcessId) { Context context = getContext(); mContentViewCore = new ContentViewCore(context); - ContentView view = ContentView.newInstance(context, mContentViewCore); + ContentView view = new ContentView(context, mContentViewCore); mContentViewCore.initialize(view, view, webContents, mWindow); mWebContents = mContentViewCore.getWebContents(); mNavigationController = mWebContents.getNavigationController();
diff --git a/chromecast/browser/cast_content_browser_client.cc b/chromecast/browser/cast_content_browser_client.cc index 3234a59..beae0d3 100644 --- a/chromecast/browser/cast_content_browser_client.cc +++ b/chromecast/browser/cast_content_browser_client.cc
@@ -40,6 +40,7 @@ #include "content/public/common/web_preferences.h" #include "gin/v8_initializer.h" #include "net/ssl/ssl_cert_request_info.h" +#include "net/url_request/url_request_context_getter.h" #include "ui/gl/gl_switches.h" #if defined(OS_ANDROID) @@ -74,22 +75,43 @@ void CastContentBrowserClient::RenderProcessWillLaunch( content::RenderProcessHost* host) { - scoped_refptr<content::BrowserMessageFilter> network_hints_message_filter( - new network_hints::NetworkHintsMessageFilter( - url_request_context_factory_->host_resolver())); - host->AddFilter(network_hints_message_filter.get()); #if !defined(OS_ANDROID) scoped_refptr<media::CmaMessageFilterHost> cma_message_filter( new media::CmaMessageFilterHost(host->GetID())); host->AddFilter(cma_message_filter.get()); #endif // !defined(OS_ANDROID) + // Forcibly trigger I/O-thread URLRequestContext initialization before + // getting HostResolver. + content::BrowserThread::PostTaskAndReplyWithResult( + content::BrowserThread::IO, FROM_HERE, + base::Bind(&net::URLRequestContextGetter::GetURLRequestContext, + base::Unretained( + url_request_context_factory_->GetSystemGetter())), + base::Bind(&CastContentBrowserClient::AddNetworkHintsMessageFilter, + base::Unretained(this), host->GetID())); + auto extra_filters = PlatformGetBrowserMessageFilters(); for (auto const& filter : extra_filters) { host->AddFilter(filter.get()); } } +void CastContentBrowserClient::AddNetworkHintsMessageFilter( + int render_process_id, net::URLRequestContext* context) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + + content::RenderProcessHost* host = + content::RenderProcessHost::FromID(render_process_id); + if (!host) + return; + + scoped_refptr<content::BrowserMessageFilter> network_hints_message_filter( + new network_hints::NetworkHintsMessageFilter( + url_request_context_factory_->host_resolver())); + host->AddFilter(network_hints_message_filter.get()); +} + net::URLRequestContextGetter* CastContentBrowserClient::CreateRequestContext( content::BrowserContext* browser_context, content::ProtocolHandlerMap* protocol_handlers,
diff --git a/chromecast/browser/cast_content_browser_client.h b/chromecast/browser/cast_content_browser_client.h index c35bc2ec..193a090f 100644 --- a/chromecast/browser/cast_content_browser_client.h +++ b/chromecast/browser/cast_content_browser_client.h
@@ -19,6 +19,10 @@ class BrowserMessageFilter; } +namespace net { +class HostResolver; +} + namespace chromecast { namespace shell { @@ -99,6 +103,9 @@ #endif // defined(OS_ANDROID) && defined(VIDEO_HOLE) private: + void AddNetworkHintsMessageFilter(int render_process_id, + net::URLRequestContext* context); + net::X509Certificate* SelectClientCertificateOnIOThread( GURL requesting_url, int render_process_id);
diff --git a/chromecast/browser/devtools/cast_dev_tools_delegate.cc b/chromecast/browser/devtools/cast_dev_tools_delegate.cc index d5bbf17..41447275 100644 --- a/chromecast/browser/devtools/cast_dev_tools_delegate.cc +++ b/chromecast/browser/devtools/cast_dev_tools_delegate.cc
@@ -7,6 +7,7 @@ #include "base/files/file_path.h" #include "base/macros.h" #include "base/strings/utf_string_conversions.h" +#include "components/devtools_discovery/devtools_discovery_manager.h" #include "content/public/browser/devtools_agent_host.h" #include "content/public/browser/devtools_target.h" #include "content/public/browser/favicon_status.h" @@ -20,67 +21,6 @@ namespace chromecast { namespace shell { -namespace { - -const char kTargetTypePage[] = "page"; -const char kTargetTypeServiceWorker[] = "service_worker"; -const char kTargetTypeSharedWorker[] = "worker"; -const char kTargetTypeOther[] = "other"; - -class Target : public content::DevToolsTarget { - public: - explicit Target(scoped_refptr<content::DevToolsAgentHost> agent_host); - - std::string GetId() const override { return agent_host_->GetId(); } - std::string GetParentId() const override { return std::string(); } - std::string GetType() const override { - switch (agent_host_->GetType()) { - case content::DevToolsAgentHost::TYPE_WEB_CONTENTS: - return kTargetTypePage; - case content::DevToolsAgentHost::TYPE_SERVICE_WORKER: - return kTargetTypeServiceWorker; - case content::DevToolsAgentHost::TYPE_SHARED_WORKER: - return kTargetTypeSharedWorker; - default: - break; - } - return kTargetTypeOther; - } - std::string GetTitle() const override { return agent_host_->GetTitle(); } - std::string GetDescription() const override { return std::string(); } - GURL GetURL() const override { return agent_host_->GetURL(); } - GURL GetFaviconURL() const override { return favicon_url_; } - base::TimeTicks GetLastActivityTime() const override { - return last_activity_time_; - } - bool IsAttached() const override { return agent_host_->IsAttached(); } - scoped_refptr<content::DevToolsAgentHost> GetAgentHost() const override { - return agent_host_; - } - bool Activate() const override { return agent_host_->Activate(); } - bool Close() const override { return agent_host_->Close(); } - - private: - scoped_refptr<content::DevToolsAgentHost> agent_host_; - GURL favicon_url_; - base::TimeTicks last_activity_time_; - - DISALLOW_COPY_AND_ASSIGN(Target); -}; - -Target::Target(scoped_refptr<content::DevToolsAgentHost> agent_host) - : agent_host_(agent_host) { - if (content::WebContents* web_contents = agent_host_->GetWebContents()) { - content::NavigationController& controller = web_contents->GetController(); - content::NavigationEntry* entry = controller.GetActiveEntry(); - if (entry != NULL && entry->GetURL().is_valid()) - favicon_url_ = entry->GetFavicon().url; - last_activity_time_ = web_contents->GetLastActiveTime(); - } -} - -} // namespace - // CastDevToolsDelegate ----------------------------------------------------- CastDevToolsDelegate::CastDevToolsDelegate() { @@ -129,12 +69,10 @@ void CastDevToolsManagerDelegate::EnumerateTargets(TargetCallback callback) { TargetList targets; - content::DevToolsAgentHost::List agents = - content::DevToolsAgentHost::GetOrCreateAll(); - for (content::DevToolsAgentHost::List::iterator it = agents.begin(); - it != agents.end(); ++it) { - targets.push_back(new Target(*it)); - } + devtools_discovery::DevToolsDiscoveryManager* discovery_manager = + devtools_discovery::DevToolsDiscoveryManager::GetInstance(); + for (const auto& descriptor : discovery_manager->GetDescriptors()) + targets.push_back(descriptor); callback.Run(targets); }
diff --git a/chromecast/browser/media/cma_message_filter_host.cc b/chromecast/browser/media/cma_message_filter_host.cc index 899b9ef..d3664c91 100644 --- a/chromecast/browser/media/cma_message_filter_host.cc +++ b/chromecast/browser/media/cma_message_filter_host.cc
@@ -442,7 +442,7 @@ } void CmaMessageFilterHost::SetPlaybackRate( - int media_id, float playback_rate) { + int media_id, double playback_rate) { DCHECK_CURRENTLY_ON(content::BrowserThread::IO); MediaPipelineHost* media_pipeline = LookupById(media_id); if (!media_pipeline)
diff --git a/chromecast/browser/media/cma_message_filter_host.h b/chromecast/browser/media/cma_message_filter_host.h index 763727c..e000574 100644 --- a/chromecast/browser/media/cma_message_filter_host.h +++ b/chromecast/browser/media/cma_message_filter_host.h
@@ -77,7 +77,7 @@ void StartPlayingFrom(int media_id, base::TimeDelta time); void Flush(int media_id); void Stop(int media_id); - void SetPlaybackRate(int media_id, float playback_rate); + void SetPlaybackRate(int media_id, double playback_rate); void SetVolume(int media_id, TrackId track_id, float volume); void NotifyPipeWrite(int media_id, TrackId track_id); void NotifyExternalSurface(int surface_id,
diff --git a/chromecast/browser/media/media_pipeline_host.cc b/chromecast/browser/media/media_pipeline_host.cc index ff97612..ff3eb69 100644 --- a/chromecast/browser/media/media_pipeline_host.cc +++ b/chromecast/browser/media/media_pipeline_host.cc
@@ -142,7 +142,7 @@ media_pipeline_->Stop(); } -void MediaPipelineHost::SetPlaybackRate(float playback_rate) { +void MediaPipelineHost::SetPlaybackRate(double playback_rate) { DCHECK(thread_checker_.CalledOnValidThread()); media_pipeline_->SetPlaybackRate(playback_rate); }
diff --git a/chromecast/browser/media/media_pipeline_host.h b/chromecast/browser/media/media_pipeline_host.h index 1dea01e..dc15db9 100644 --- a/chromecast/browser/media/media_pipeline_host.h +++ b/chromecast/browser/media/media_pipeline_host.h
@@ -59,7 +59,7 @@ void Flush(const ::media::PipelineStatusCB& status_cb); void Stop(); - void SetPlaybackRate(float playback_rate); + void SetPlaybackRate(double playback_rate); void SetVolume(TrackId track_id, float playback_rate); void SetCdm(BrowserCdmCast* cdm);
diff --git a/chromecast/build/args.gn b/chromecast/build/args.gn new file mode 100644 index 0000000..1624b0a --- /dev/null +++ b/chromecast/build/args.gn
@@ -0,0 +1,14 @@ +# Build arguments for Chromecast. +# Copy the contents of this file to gn args. + +root_extra_deps = [ "//chromecast" ] + +# Flags set from build/common.gypi +enable_mpeg2ts_stream_parser = true +ffmpeg_branding = "ChromeOS" +proprietary_codecs = true +enable_browser_cdms = true +# TODO(halliwell): look into supporting Cast Ozone with GN. +# ozone_platform_cast = 1 +# TODO(gyp): Add support for blink_logging_always_on and enable it. +# blink_logging_always_on = 1
diff --git a/chromecast/chromecast.gni b/chromecast/chromecast.gni new file mode 100644 index 0000000..b6f7307b --- /dev/null +++ b/chromecast/chromecast.gni
@@ -0,0 +1,10 @@ +# 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. + +declare_args() { + # is_chromecast_chrome_branded is set to true for an official build + is_chromecast_chrome_branded = false + + use_playready = false +}
diff --git a/chromecast/chromecast.gyp b/chromecast/chromecast.gyp index 15b31b40..5c2e49c 100644 --- a/chromecast/chromecast.gyp +++ b/chromecast/chromecast.gyp
@@ -38,6 +38,7 @@ 'sources': [ 'public/cast_egl_platform.h', 'public/cast_egl_platform_shlib.h', + 'public/cast_sys_info.h', 'public/chromecast_export.h', 'public/graphics_properties_shlib.h' ], @@ -59,7 +60,9 @@ 'base/metrics/cast_metrics_helper.cc', 'base/metrics/cast_metrics_helper.h', 'base/metrics/grouped_histogram.cc', - 'base/metrics/grouped_histogram.h' + 'base/metrics/grouped_histogram.h', + 'base/serializers.cc', + 'base/serializers.h' ], }, # end of target 'cast_base' { @@ -177,6 +180,7 @@ 'cast_net', 'cast_shell_pak', 'cast_shell_resources', + 'cast_sys_info', 'cast_version_header', 'chromecast_locales.gyp:chromecast_locales_pak', 'chromecast_locales.gyp:chromecast_settings', @@ -186,6 +190,7 @@ '../components/components.gyp:cdm_renderer', '../components/components.gyp:component_metrics_proto', '../components/components.gyp:crash_component', + '../components/components.gyp:devtools_discovery', '../components/components.gyp:devtools_http_handler', '../components/components.gyp:network_hints_browser', '../components/components.gyp:network_hints_renderer', @@ -313,6 +318,26 @@ ], }, { + 'target_name': 'cast_sys_info', + 'type': '<(component)', + 'dependencies': [ + 'cast_public_api', + '../base/base.gyp:base', + ], + 'sources': [ + 'base/cast_sys_info_util.h', + 'base/cast_sys_info_dummy.cc', + 'base/cast_sys_info_dummy.h', + ], + 'conditions': [ + ['chromecast_branding!="Chrome"', { + 'sources': [ + 'base/cast_sys_info_util_simple.cc', + ], + }], + ], + }, # end of target 'cast_sys_info' + { 'target_name': 'cast_version_header', 'type': 'none', 'direct_dependent_settings': { @@ -573,9 +598,14 @@ 'sources': [ 'app/cast_main.cc', ], - # TODO(dougsteed): remove when Chromecast moves to boringssl. - # Allow the cast shell to find the NSS module in the same directory. 'ldflags': [ + # Allow OEMs to override default libraries that are shipped with + # cast receiver package by installed OEM-specific libraries in + # /oem_cast_shlib. + '-Wl,-rpath=/oem_cast_shlib', + # TODO(dougsteed): remove when Chromecast moves to boringssl. + # Allow the cast shell to find the NSS module in the same + # directory. '-Wl,-rpath=\$$ORIGIN' ], },
diff --git a/chromecast/chromecast_tests.gypi b/chromecast/chromecast_tests.gypi index 09bcb52..26c5762 100644 --- a/chromecast/chromecast_tests.gypi +++ b/chromecast/chromecast_tests.gypi
@@ -3,8 +3,23 @@ # found in the LICENSE file. { + 'variables': { + 'chromium_code': 1 + }, 'targets': [ { + 'target_name': 'cast_base_unittests', + 'type': '<(gtest_target_type)', + 'dependencies': [ + 'chromecast.gyp:cast_base', + '../testing/gtest.gyp:gtest', + '../testing/gtest.gyp:gtest_main', + ], + 'sources': [ + 'base/serializers_unittest.cc', + ], + }, + { 'target_name': 'cast_tests', 'type': 'none', 'dependencies': [ @@ -23,8 +38,8 @@ 'target_name': 'cast_test_generator', 'type': 'none', 'dependencies': [ + 'cast_base_unittests', '../base/base.gyp:base_unittests', - '../third_party/cacheinvalidation/cacheinvalidation.gyp:cacheinvalidation_unittests', '../content/content_shell_and_tests.gyp:content_unittests', '../crypto/crypto.gyp:crypto_unittests', '../ipc/ipc.gyp:ipc_tests', @@ -34,6 +49,7 @@ '../sandbox/sandbox.gyp:sandbox_linux_unittests', '../sql/sql.gyp:sql_unittests', '../sync/sync.gyp:sync_unit_tests', + '../third_party/cacheinvalidation/cacheinvalidation.gyp:cacheinvalidation_unittests', '../ui/base/ui_base_tests.gyp:ui_base_unittests', '../url/url.gyp:url_unittests', ],
diff --git a/chromecast/common/cast_content_client.cc b/chromecast/common/cast_content_client.cc index e93b7d2a..4772fdd5 100644 --- a/chromecast/common/cast_content_client.cc +++ b/chromecast/common/cast_content_client.cc
@@ -4,6 +4,8 @@ #include "chromecast/common/cast_content_client.h" +#include "base/strings/stringprintf.h" +#include "base/sys_info.h" #include "chromecast/common/version.h" #include "content/public/common/user_agent.h" #include "ui/base/l10n/l10n_util.h" @@ -12,9 +14,56 @@ namespace chromecast { namespace shell { +namespace { + +#if defined(OS_ANDROID) +std::string BuildAndroidOsInfo() { + int32 os_major_version = 0; + int32 os_minor_version = 0; + int32 os_bugfix_version = 0; + base::SysInfo::OperatingSystemVersionNumbers(&os_major_version, + &os_minor_version, + &os_bugfix_version); + + std::string android_version_str; + base::StringAppendF( + &android_version_str, "%d.%d", os_major_version, os_minor_version); + if (os_bugfix_version != 0) + base::StringAppendF(&android_version_str, ".%d", os_bugfix_version); + + std::string android_info_str; + // Append the build ID. + std::string android_build_id = base::SysInfo::GetAndroidBuildID(); + if (android_build_id.size() > 0) + android_info_str += "; Build/" + android_build_id; + + std::string os_info; + base::StringAppendF( + &os_info, + "Android %s%s", + android_version_str.c_str(), + android_info_str.c_str()); + return os_info; +} +#endif + +} // namespace + std::string GetUserAgent() { std::string product = "Chrome/" PRODUCT_VERSION; - return content::BuildUserAgentFromProduct(product) + + std::string os_info; + base::StringAppendF( + &os_info, + "%s%s", +#if defined(OS_ANDROID) + "Linux; ", + BuildAndroidOsInfo().c_str() +#else + "X11; ", + content::BuildOSCpuInfo().c_str() +#endif + ); + return content::BuildUserAgentFromOSAndProduct(os_info, product) + " CrKey/" CAST_BUILD_REVISION; }
diff --git a/chromecast/common/media/cma_messages.h b/chromecast/common/media/cma_messages.h index 414913b2..3fbed3db 100644 --- a/chromecast/common/media/cma_messages.h +++ b/chromecast/common/media/cma_messages.h
@@ -41,7 +41,7 @@ int /* Media pipeline ID */) IPC_MESSAGE_CONTROL2(CmaHostMsg_SetPlaybackRate, int /* Media pipeline ID */, - float /* Playback rate */) + double /* Playback rate */) IPC_MESSAGE_CONTROL3(CmaHostMsg_CreateAvPipe, int /* Media pipeline ID */,
diff --git a/chromecast/crash/android/crash_handler.cc b/chromecast/crash/android/crash_handler.cc index 3b195464..1c42dfa 100644 --- a/chromecast/crash/android/crash_handler.cc +++ b/chromecast/crash/android/crash_handler.cc
@@ -26,15 +26,13 @@ chromecast::CrashHandler* g_crash_handler = NULL; -// ExceptionHandler requires a HandlerCallback as a function pointer. This -// function exists to proxy into the global CrashHandler instance. -bool HandleCrash(const void* /* crash_context */, - size_t /* crash_context_size */, - void* /* context */) { +bool HandleCrash(void* /* crash_context */) { DCHECK(g_crash_handler); g_crash_handler->UploadCrashDumps(); - // Let the exception continue to propagate up to the system. + // TODO(gunsch): clean up the ATV crash handling code. + // Don't write another minidump. Chrome's default ExceptionHandler has already + // written a minidump by this point in the crash handling sequence. return false; } @@ -100,8 +98,7 @@ // Dummy MinidumpDescriptor just to start up another ExceptionHandler. google_breakpad::MinidumpDescriptor dummy(crash_dump_path_.value()); crash_uploader_.reset(new google_breakpad::ExceptionHandler( - dummy, NULL, NULL, NULL, true, -1)); - crash_uploader_->set_crash_handler(&::HandleCrash); + dummy, &HandleCrash, NULL, NULL, true, -1)); breakpad::InitCrashReporter(process_type);
diff --git a/chromecast/media/BUILD.gn b/chromecast/media/BUILD.gn new file mode 100644 index 0000000..6a8c6a6 --- /dev/null +++ b/chromecast/media/BUILD.gn
@@ -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. + +import("//testing/test.gni") + +group("media") { + deps = [ + "//chromecast/media/base", + "//chromecast/media/cdm", + "//chromecast/media/cma", + ] +} + +test("unittests") { + sources = [ + "//media/base", + "cma/backend/audio_video_pipeline_device_unittest.cc", + "cma/base/balanced_media_task_runner_unittest.cc", + "cma/base/buffering_controller_unittest.cc", + "cma/base/buffering_frame_provider_unittest.cc", + "cma/filters/demuxer_stream_adapter_unittest.cc", + "cma/ipc/media_message_fifo_unittest.cc", + "cma/ipc/media_message_unittest.cc", + "cma/ipc_streamer/av_streamer_unittest.cc", + "cma/pipeline/audio_video_pipeline_impl_unittest.cc", + "cma/test/frame_generator_for_test.cc", + "cma/test/frame_generator_for_test.h", + "cma/test/frame_segmenter_for_test.cc", + "cma/test/frame_segmenter_for_test.h", + "cma/test/media_component_device_feeder_for_test.cc", + "cma/test/media_component_device_feeder_for_test.h", + "cma/test/mock_frame_consumer.cc", + "cma/test/mock_frame_consumer.h", + "cma/test/mock_frame_provider.cc", + "cma/test/mock_frame_provider.h", + "cma/test/run_all_unittests.cc", + ] + + deps = [ + ":media", + "//base", + "//base:i18n", + "//base/test:test_support", + "//chromecast/base/metrics:test_support", + "//media", + "//media/base:test_support", + "//testing/gmock", + "//testing/gtest", + ] + + configs += [ "//chromecast:config" ] +}
diff --git a/chromecast/media/base/BUILD.gn b/chromecast/media/base/BUILD.gn new file mode 100644 index 0000000..64992f33 --- /dev/null +++ b/chromecast/media/base/BUILD.gn
@@ -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. + +import("//build/config/crypto.gni") +import("//chromecast/chromecast.gni") + +source_set("base") { + sources = [ + "decrypt_context.cc", + "decrypt_context.h", + "decrypt_context_clearkey.cc", + "decrypt_context_clearkey.h", + "key_systems_common.cc", + "key_systems_common.h", + "media_caps.cc", + "media_caps.h", + "switching_media_renderer.cc", + "switching_media_renderer.h", + ] + + deps = [ + "//base", + "//crypto", + "//crypto:platform", + "//third_party/widevine/cdm:version_h", + ] + + configs += [ "//chromecast:config" ] + + if (is_chromecast_chrome_branded) { + deps += [ + # TODO(gyp): add dependency on internal/chromecast_internal:media_base_internal + ] + } else { + sources += [ "key_systems_common_simple.cc" ] + } +}
diff --git a/chromecast/media/base/switching_media_renderer.cc b/chromecast/media/base/switching_media_renderer.cc index 79b7d1ef..b0a12c3 100644 --- a/chromecast/media/base/switching_media_renderer.cc +++ b/chromecast/media/base/switching_media_renderer.cc
@@ -78,7 +78,7 @@ GetRenderer()->StartPlayingFrom(time); } -void SwitchingMediaRenderer::SetPlaybackRate(float playback_rate) { +void SwitchingMediaRenderer::SetPlaybackRate(double playback_rate) { GetRenderer()->SetPlaybackRate(playback_rate); }
diff --git a/chromecast/media/base/switching_media_renderer.h b/chromecast/media/base/switching_media_renderer.h index 2739f400..c32b1d4 100644 --- a/chromecast/media/base/switching_media_renderer.h +++ b/chromecast/media/base/switching_media_renderer.h
@@ -43,7 +43,7 @@ const ::media::CdmAttachedCB& cdm_attached_cb) override; void Flush(const base::Closure& flush_cb) override; void StartPlayingFrom(base::TimeDelta time) override; - void SetPlaybackRate(float playback_rate) override; + void SetPlaybackRate(double playback_rate) override; void SetVolume(float volume) override; base::TimeDelta GetMediaTime() override; bool HasAudio() override;
diff --git a/chromecast/media/cdm/BUILD.gn b/chromecast/media/cdm/BUILD.gn new file mode 100644 index 0000000..1a929d0 --- /dev/null +++ b/chromecast/media/cdm/BUILD.gn
@@ -0,0 +1,18 @@ +# 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("cdm") { + sources = [ + "browser_cdm_cast.cc", + "browser_cdm_cast.h", + ] + + deps = [ + "//base", + "//chromecast/media/base", + "//media", + ] + + configs += [ "//chromecast:config" ] +}
diff --git a/chromecast/media/cma/BUILD.gn b/chromecast/media/cma/BUILD.gn new file mode 100644 index 0000000..c9da6296f --- /dev/null +++ b/chromecast/media/cma/BUILD.gn
@@ -0,0 +1,14 @@ +# 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. + +group("cma") { + deps = [ + "//chromecast/media/cma/backend", + "//chromecast/media/cma/base", + "//chromecast/media/cma/filters", + "//chromecast/media/cma/ipc", + "//chromecast/media/cma/ipc_streamer", + "//chromecast/media/cma/pipeline", + ] +}
diff --git a/chromecast/media/cma/backend/BUILD.gn b/chromecast/media/cma/backend/BUILD.gn new file mode 100644 index 0000000..6526a1f7 --- /dev/null +++ b/chromecast/media/cma/backend/BUILD.gn
@@ -0,0 +1,37 @@ +# 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("backend") { + sources = [ + "audio_pipeline_device.cc", + "audio_pipeline_device.h", + "media_clock_device.cc", + "media_clock_device.h", + "media_component_device.cc", + "media_component_device.h", + "media_pipeline_device.cc", + "media_pipeline_device.h", + "media_pipeline_device_fake.cc", + "media_pipeline_device_fake.h", + "media_pipeline_device_fake_factory.cc", + "media_pipeline_device_params.cc", + "media_pipeline_device_params.h", + "video_pipeline_device.cc", + "video_pipeline_device.h", + "video_plane.cc", + "video_plane.h", + "video_plane_fake.cc", + "video_plane_fake.h", + "video_plane_fake_factory.cc", + ] + + deps = [ + "//base", + "//chromecast/media/base", + "//chromecast/media/cma/base", + "//media", + ] + + configs += [ "//chromecast:config" ] +}
diff --git a/chromecast/media/cma/base/BUILD.gn b/chromecast/media/cma/base/BUILD.gn new file mode 100644 index 0000000..b919439 --- /dev/null +++ b/chromecast/media/cma/base/BUILD.gn
@@ -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. + +source_set("base") { + sources = [ + "balanced_media_task_runner_factory.cc", + "balanced_media_task_runner_factory.h", + "buffering_controller.cc", + "buffering_controller.h", + "buffering_defs.cc", + "buffering_defs.h", + "buffering_frame_provider.cc", + "buffering_frame_provider.h", + "buffering_state.cc", + "buffering_state.h", + "cma_logging.h", + "coded_frame_provider.cc", + "coded_frame_provider.h", + "decoder_buffer_adapter.cc", + "decoder_buffer_adapter.h", + "decoder_buffer_base.cc", + "decoder_buffer_base.h", + "media_task_runner.cc", + "media_task_runner.h", + ] + + deps = [ + "//base", + "//chromecast/base", + "//media", + ] + + configs += [ "//chromecast:config" ] +}
diff --git a/chromecast/media/cma/filters/BUILD.gn b/chromecast/media/cma/filters/BUILD.gn new file mode 100644 index 0000000..6050755 --- /dev/null +++ b/chromecast/media/cma/filters/BUILD.gn
@@ -0,0 +1,20 @@ +# 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("filters") { + sources = [ + "cma_renderer.cc", + "cma_renderer.h", + "demuxer_stream_adapter.cc", + "demuxer_stream_adapter.h", + ] + + deps = [ + "//base", + "//chromecast/media/cma/base", + "//media", + ] + + configs += [ "//chromecast:config" ] +}
diff --git a/chromecast/media/cma/filters/cma_renderer.cc b/chromecast/media/cma/filters/cma_renderer.cc index bc29b4f..0de06343 100644 --- a/chromecast/media/cma/filters/cma_renderer.cc +++ b/chromecast/media/cma/filters/cma_renderer.cc
@@ -55,7 +55,7 @@ initial_video_hole_created_(false), time_interpolator_( new ::media::TimeDeltaInterpolator(&default_tick_clock_)), - playback_rate_(1.0f), + playback_rate_(1.0), weak_factory_(this) { weak_this_ = weak_factory_.GetWeakPtr(); thread_checker_.DetachFromThread(); @@ -185,7 +185,7 @@ CompleteStateTransition(kPlaying); } -void CmaRenderer::SetPlaybackRate(float playback_rate) { +void CmaRenderer::SetPlaybackRate(double playback_rate) { CMALOG(kLogControl) << __FUNCTION__ << ": " << playback_rate; DCHECK(thread_checker_.CalledOnValidThread()); media_pipeline_->SetPlaybackRate(playback_rate);
diff --git a/chromecast/media/cma/filters/cma_renderer.h b/chromecast/media/cma/filters/cma_renderer.h index 25fec41..d33699e 100644 --- a/chromecast/media/cma/filters/cma_renderer.h +++ b/chromecast/media/cma/filters/cma_renderer.h
@@ -50,7 +50,7 @@ const base::Closure& waiting_for_decryption_key_cb) override; void Flush(const base::Closure& flush_cb) override; void StartPlayingFrom(base::TimeDelta time) override; - void SetPlaybackRate(float playback_rate) override; + void SetPlaybackRate(double playback_rate) override; void SetVolume(float volume) override; base::TimeDelta GetMediaTime() override; bool HasAudio() override; @@ -137,7 +137,7 @@ // as playback progresses. scoped_ptr< ::media::TimeDeltaInterpolator> time_interpolator_; - float playback_rate_; + double playback_rate_; base::WeakPtr<CmaRenderer> weak_this_; base::WeakPtrFactory<CmaRenderer> weak_factory_;
diff --git a/chromecast/media/cma/ipc/BUILD.gn b/chromecast/media/cma/ipc/BUILD.gn new file mode 100644 index 0000000..037b3bd --- /dev/null +++ b/chromecast/media/cma/ipc/BUILD.gn
@@ -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. + +source_set("ipc") { + sources = [ + "media_memory_chunk.cc", + "media_memory_chunk.h", + "media_message.cc", + "media_message.h", + "media_message_fifo.cc", + "media_message_fifo.h", + "media_message_type.h", + ] + + deps = [ + "//base", + ] + + configs += [ "//chromecast:config" ] +}
diff --git a/chromecast/media/cma/ipc_streamer/BUILD.gn b/chromecast/media/cma/ipc_streamer/BUILD.gn new file mode 100644 index 0000000..aa87709 --- /dev/null +++ b/chromecast/media/cma/ipc_streamer/BUILD.gn
@@ -0,0 +1,28 @@ +# 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("ipc_streamer") { + sources = [ + "audio_decoder_config_marshaller.cc", + "audio_decoder_config_marshaller.h", + "av_streamer_proxy.cc", + "av_streamer_proxy.h", + "coded_frame_provider_host.cc", + "coded_frame_provider_host.h", + "decoder_buffer_base_marshaller.cc", + "decoder_buffer_base_marshaller.h", + "decrypt_config_marshaller.cc", + "decrypt_config_marshaller.h", + "video_decoder_config_marshaller.cc", + "video_decoder_config_marshaller.h", + ] + + deps = [ + "//base", + "//chromecast/media/cma/base", + "//media", + ] + + configs += [ "//chromecast:config" ] +}
diff --git a/chromecast/media/cma/pipeline/BUILD.gn b/chromecast/media/cma/pipeline/BUILD.gn new file mode 100644 index 0000000..f82a8d9a --- /dev/null +++ b/chromecast/media/cma/pipeline/BUILD.gn
@@ -0,0 +1,44 @@ +# 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("pipeline") { + sources = [ + "audio_pipeline.cc", + "audio_pipeline.h", + "audio_pipeline_impl.cc", + "audio_pipeline_impl.h", + "av_pipeline_client.cc", + "av_pipeline_client.h", + "av_pipeline_impl.cc", + "av_pipeline_impl.h", + "decrypt_util.cc", + "decrypt_util.h", + "load_type.h", + "media_pipeline.h", + "media_pipeline_client.cc", + "media_pipeline_client.h", + "media_pipeline_impl.cc", + "media_pipeline_impl.h", + "video_pipeline.cc", + "video_pipeline.h", + "video_pipeline_client.cc", + "video_pipeline_client.h", + "video_pipeline_impl.cc", + "video_pipeline_impl.h", + ] + + deps = [ + "//base", + "//chromecast/media/cma/backend", + "//chromecast/media/cma/base", + "//chromecast/media/base", + "//chromecast/media/cdm", + "//crypto", + "//crypto:platform", + "//media", + "//third_party/boringssl", + ] + + configs += [ "//chromecast:config" ] +}
diff --git a/chromecast/media/cma/pipeline/media_pipeline.h b/chromecast/media/cma/pipeline/media_pipeline.h index 2b82c4f1..6b127cb2 100644 --- a/chromecast/media/cma/pipeline/media_pipeline.h +++ b/chromecast/media/cma/pipeline/media_pipeline.h
@@ -57,7 +57,7 @@ virtual void Stop() = 0; // Set the playback rate. - virtual void SetPlaybackRate(float playback_rate) = 0; + virtual void SetPlaybackRate(double playback_rate) = 0; private: DISALLOW_COPY_AND_ASSIGN(MediaPipeline);
diff --git a/chromecast/media/cma/pipeline/media_pipeline_impl.cc b/chromecast/media/cma/pipeline/media_pipeline_impl.cc index c713f9a..ced7df14 100644 --- a/chromecast/media/cma/pipeline/media_pipeline_impl.cc +++ b/chromecast/media/cma/pipeline/media_pipeline_impl.cc
@@ -263,7 +263,7 @@ video_pipeline_->Stop(); } -void MediaPipelineImpl::SetPlaybackRate(float rate) { +void MediaPipelineImpl::SetPlaybackRate(double rate) { CMALOG(kLogControl) << __FUNCTION__ << " rate=" << rate; DCHECK(thread_checker_.CalledOnValidThread()); target_playback_rate_ = rate;
diff --git a/chromecast/media/cma/pipeline/media_pipeline_impl.h b/chromecast/media/cma/pipeline/media_pipeline_impl.h index 166b8f9..eb14a555b 100644 --- a/chromecast/media/cma/pipeline/media_pipeline_impl.h +++ b/chromecast/media/cma/pipeline/media_pipeline_impl.h
@@ -51,7 +51,7 @@ void StartPlayingFrom(base::TimeDelta time) override; void Flush(const ::media::PipelineStatusCB& status_cb) override; void Stop() override; - void SetPlaybackRate(float playback_rate) override; + void SetPlaybackRate(double playback_rate) override; AudioPipelineImpl* GetAudioPipelineImpl() const; VideoPipelineImpl* GetVideoPipelineImpl() const;
diff --git a/chromecast/public/DEPS b/chromecast/public/DEPS new file mode 100644 index 0000000..30d9ea2d --- /dev/null +++ b/chromecast/public/DEPS
@@ -0,0 +1,8 @@ +include_rules = [ + # chromecast/public should not depend on anything Chromium specific + "-chromecast", + "-base", + "-content", + "-net", + "-ui", +]
diff --git a/chromecast/public/cast_sys_info.h b/chromecast/public/cast_sys_info.h new file mode 100644 index 0000000..95d1da6 --- /dev/null +++ b/chromecast/public/cast_sys_info.h
@@ -0,0 +1,62 @@ +// 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 CHROMECAST_PUBLIC_CAST_SYS_INFO_H_ +#define CHROMECAST_PUBLIC_CAST_SYS_INFO_H_ + +#include <string> +#include <vector> + +namespace chromecast { + +// Pure abstract interface for system information which is accessed by other +// processes as well as cast_shell browser process. All information should be +// immutable. +// It should be possible to instantiate multiple instances of CastSysInfo and +// should be able to be instantiated at any point in the startup process. Other +// processes must be able to create an instance of CastSysInfo. +class CastSysInfo { + public: + enum BuildType { + BUILD_ENG, + BUILD_BETA, + BUILD_PRODUCTION, + }; + + virtual ~CastSysInfo() {} + + // Returns the system build type. + virtual BuildType GetBuildType() = 0; + // Returns release channel of system. + virtual std::string GetSystemReleaseChannel() = 0; + // Returns serial number of the device. + virtual std::string GetSerialNumber() = 0; + // Returns product code name of the device. + virtual std::string GetProductName() = 0; + // Returns model name of device (eg: Chromecast, Nexus Player, ...). + virtual std::string GetDeviceModel() = 0; + // Returns the board's name. + virtual std::string GetBoardName() = 0; + // Returns the revision of board (eg: 514, ...). + virtual std::string GetBoardRevision() = 0; + // Returns device manufacturer (eg: Google, ...). + virtual std::string GetManufacturer() = 0; + // Returns the system's build number (eg: 100, 20000 ...). + // This describes system version which may be different with + // CAST_BUILD_NUMBER. + virtual std::string GetSystemBuildNumber() = 0; + + // Returns default country and locale baked from the factory. + virtual std::string GetFactoryCountry() = 0; + virtual std::string GetFactoryLocale(std::string* second_locale) = 0; + + // Returns the name of the wifi interface used to connect to the internet. + virtual std::string GetWifiInterface() = 0; + // Returns the name of the software AP interface. + virtual std::string GetApInterface() = 0; +}; + +} // namespace chromecast + +#endif // CHROMECAST_PUBLIC_CAST_SYS_INFO_H_
diff --git a/chromecast/renderer/key_systems_cast.cc b/chromecast/renderer/key_systems_cast.cc index 5d3ad1c..c9cae76 100644 --- a/chromecast/renderer/key_systems_cast.cc +++ b/chromecast/renderer/key_systems_cast.cc
@@ -27,11 +27,13 @@ ::media::EME_CODEC_MP4_AAC | ::media::EME_CODEC_MP4_AVC1; info.max_audio_robustness = ::media::EmeRobustness::EMPTY; info.max_video_robustness = ::media::EmeRobustness::EMPTY; - info.persistent_license_support = ::media::EME_SESSION_TYPE_NOT_SUPPORTED; + info.persistent_license_support = + ::media::EmeSessionTypeSupport::NOT_SUPPORTED; info.persistent_release_message_support = - ::media::EME_SESSION_TYPE_NOT_SUPPORTED; - info.persistent_state_support = ::media::EME_FEATURE_ALWAYS_ENABLED; - info.distinctive_identifier_support = ::media::EME_FEATURE_ALWAYS_ENABLED; + ::media::EmeSessionTypeSupport::NOT_SUPPORTED; + info.persistent_state_support = ::media::EmeFeatureSupport::ALWAYS_ENABLED; + info.distinctive_identifier_support = + ::media::EmeFeatureSupport::ALWAYS_ENABLED; key_systems_info->push_back(info); } @@ -39,15 +41,16 @@ std::vector<::media::KeySystemInfo>* key_systems_info) { #if defined(WIDEVINE_CDM_AVAILABLE) AddWidevineWithCodecs( - cdm::WIDEVINE, - ::media::EME_CODEC_MP4_AAC | ::media::EME_CODEC_MP4_AVC1, - ::media::EmeRobustness::HW_SECURE_ALL, // Max audio robustness. - ::media::EmeRobustness::HW_SECURE_ALL, // Max video robustness. - ::media::EME_SESSION_TYPE_NOT_SUPPORTED, // persistent-license. - ::media::EME_SESSION_TYPE_NOT_SUPPORTED, // persistent-release-message. + cdm::WIDEVINE, ::media::EME_CODEC_MP4_AAC | ::media::EME_CODEC_MP4_AVC1, + ::media::EmeRobustness::HW_SECURE_ALL, // Max audio robustness. + ::media::EmeRobustness::HW_SECURE_ALL, // Max video robustness. + ::media::EmeSessionTypeSupport::NOT_SUPPORTED, // persistent-license. + ::media::EmeSessionTypeSupport:: + NOT_SUPPORTED, // persistent-release-message. // Note: On Chromecast, all CDMs may have persistent state. - ::media::EME_FEATURE_ALWAYS_ENABLED, // Persistent state. - ::media::EME_FEATURE_ALWAYS_ENABLED, // Distinctive identifier. + ::media::EmeFeatureSupport::ALWAYS_ENABLED, // Persistent state. + ::media::EmeFeatureSupport::ALWAYS_ENABLED, // Distinctive + // identifier. key_systems_info); #endif
diff --git a/chromecast/renderer/media/media_pipeline_proxy.cc b/chromecast/renderer/media/media_pipeline_proxy.cc index 7a454b166..830ac0f 100644 --- a/chromecast/renderer/media/media_pipeline_proxy.cc +++ b/chromecast/renderer/media/media_pipeline_proxy.cc
@@ -34,7 +34,7 @@ void StartPlayingFrom(const base::TimeDelta& time); void Flush(const ::media::PipelineStatusCB& status_cb); void Stop(); - void SetPlaybackRate(float playback_rate); + void SetPlaybackRate(double playback_rate); private: void Shutdown(); @@ -135,7 +135,7 @@ client_.error_cb.Run(::media::PIPELINE_ERROR_ABORT); } -void MediaPipelineProxyInternal::SetPlaybackRate(float playback_rate) { +void MediaPipelineProxyInternal::SetPlaybackRate(double playback_rate) { DCHECK(thread_checker_.CalledOnValidThread()); media_channel_proxy_->Send(scoped_ptr<IPC::Message>( new CmaHostMsg_SetPlaybackRate( @@ -274,7 +274,7 @@ FORWARD_ON_IO_THREAD(Stop); } -void MediaPipelineProxy::SetPlaybackRate(float playback_rate) { +void MediaPipelineProxy::SetPlaybackRate(double playback_rate) { DCHECK(thread_checker_.CalledOnValidThread()); FORWARD_ON_IO_THREAD(SetPlaybackRate, playback_rate); }
diff --git a/chromecast/renderer/media/media_pipeline_proxy.h b/chromecast/renderer/media/media_pipeline_proxy.h index 0d22795..decec65 100644 --- a/chromecast/renderer/media/media_pipeline_proxy.h +++ b/chromecast/renderer/media/media_pipeline_proxy.h
@@ -50,7 +50,7 @@ void StartPlayingFrom(base::TimeDelta time) override; void Flush(const ::media::PipelineStatusCB& status_cb) override; void Stop() override; - void SetPlaybackRate(float playback_rate) override; + void SetPlaybackRate(double playback_rate) override; private: void OnProxyFlushDone(const ::media::PipelineStatusCB& status_cb,
diff --git a/chromeos/CHROMEOS_LKGM b/chromeos/CHROMEOS_LKGM index 5f67f60..de5659fe 100644 --- a/chromeos/CHROMEOS_LKGM +++ b/chromeos/CHROMEOS_LKGM
@@ -1 +1 @@ -6993.0.0 \ No newline at end of file +6996.0.0 \ No newline at end of file
diff --git a/chromeos/dbus/bluetooth_le_advertising_manager_client.cc b/chromeos/dbus/bluetooth_le_advertising_manager_client.cc index 00f4f67..35bb36e 100644 --- a/chromeos/dbus/bluetooth_le_advertising_manager_client.cc +++ b/chromeos/dbus/bluetooth_le_advertising_manager_client.cc
@@ -27,8 +27,12 @@ : object_manager_(NULL), weak_ptr_factory_(this) {} ~BluetoothAdvertisementManagerClientImpl() override { - object_manager_->UnregisterInterface( - bluetooth_advertising_manager::kBluetoothAdvertisingManagerInterface); + // TODO(rkc): object_manager_ should not be NULL, just a hot fix till + // http://crbug.com/479430 is properly fixed. + if (object_manager_) { + object_manager_->UnregisterInterface( + bluetooth_advertising_manager::kBluetoothAdvertisingManagerInterface); + } } // BluetoothAdapterClient override.
diff --git a/chromeos/dbus/fake_shill_manager_client.cc b/chromeos/dbus/fake_shill_manager_client.cc index ef79717f..e6f97cb5 100644 --- a/chromeos/dbus/fake_shill_manager_client.cc +++ b/chromeos/dbus/fake_shill_manager_client.cc
@@ -775,6 +775,9 @@ devices->SetDeviceProperty("/device/cellular1", shill::kSupportedCarriersProperty, carrier_list); + devices->SetDeviceProperty("/device/cellular1", + shill::kSupportNetworkScanProperty, + base::FundamentalValue(true)); if (roaming_state_ == kRoamingRequired) { devices->SetDeviceProperty("/device/cellular1", shill::kProviderRequiresRoamingProperty, @@ -1049,8 +1052,7 @@ base::DictionaryValue* simlock_dict = new base::DictionaryValue; simlock_dict->Set(shill::kSIMLockEnabledProperty, new base::FundamentalValue(locked)); - // TODO(stevenjb): Investigate why non-empty value breaks UI. - std::string lock_type = ""; // shill::kSIMLockPin + std::string lock_type = shill::kSIMLockPin; simlock_dict->SetString(shill::kSIMLockTypeProperty, lock_type); simlock_dict->SetInteger(shill::kSIMLockRetriesLeftProperty, 5);
diff --git a/chromeos/dbus/nfc_client_unittest.cc b/chromeos/dbus/nfc_client_unittest.cc index 7870e48..736853b 100644 --- a/chromeos/dbus/nfc_client_unittest.cc +++ b/chromeos/dbus/nfc_client_unittest.cc
@@ -88,9 +88,9 @@ class NfcClientTest : public testing::Test { public: NfcClientTest() : response_(NULL) {} - virtual ~NfcClientTest() {} + ~NfcClientTest() override {} - virtual void SetUp() override { + void SetUp() override { // Create the mock bus. dbus::Bus::Options options; options.bus_type = dbus::Bus::SYSTEM; @@ -222,7 +222,7 @@ message_loop_.RunUntilIdle(); } - virtual void TearDown() override { + void TearDown() override { tag_client_->RemoveObserver(&mock_tag_observer_); device_client_->RemoveObserver(&mock_device_observer_); adapter_client_->RemoveObserver(&mock_adapter_observer_);
diff --git a/chromeos/dbus/shill_client_unittest_base.h b/chromeos/dbus/shill_client_unittest_base.h index d7b8cde..9b32490 100644 --- a/chromeos/dbus/shill_client_unittest_base.h +++ b/chromeos/dbus/shill_client_unittest_base.h
@@ -45,10 +45,10 @@ explicit ValueMatcher(const base::Value& value); // MatcherInterface overrides. - virtual bool MatchAndExplain(const base::Value& value, - MatchResultListener* listener) const override; - virtual void DescribeTo(::std::ostream* os) const override; - virtual void DescribeNegationTo(::std::ostream* os) const override; + bool MatchAndExplain(const base::Value& value, + MatchResultListener* listener) const override; + void DescribeTo(::std::ostream* os) const override; + void DescribeNegationTo(::std::ostream* os) const override; private: scoped_ptr<base::Value> expected_value_;
diff --git a/components/BUILD.gn b/components/BUILD.gn index c0f6ad8b..01503dc 100644 --- a/components/BUILD.gn +++ b/components/BUILD.gn
@@ -36,6 +36,7 @@ "//components/data_reduction_proxy/core/browser", "//components/data_reduction_proxy/core/common", "//components/device_event_log", + "//components/devtools_discovery", "//components/devtools_http_handler", "//components/dom_distiller/core", "//components/domain_reliability", @@ -224,6 +225,7 @@ deps += [ "//components/app_modal", "//components/browsing_data", + "//components/scheduler", ] } @@ -306,6 +308,10 @@ if (is_ios) { deps -= [ "//components/devtools_http_handler:unit_tests" ] } + + if (!is_ios) { + deps += [ "//components/scheduler:unit_tests" ] + } } repack("components_tests_pak") { @@ -328,6 +334,17 @@ ] } +if (is_android) { + import("//build/config/android/rules.gni") + + generate_jni("components_browsertests_jni_headers") { + jni_package = "components_browsertests/shell" + sources = [ + "test/android/browsertests_apk/src/org/chromium/components_browsertests_apk/ComponentsBrowserTestsActivity.java", + ] + } +} + test("components_browsertests") { sources = [ "autofill/content/browser/risk/fingerprint_browsertest.cc", @@ -363,7 +380,18 @@ data_deps = [ ":components_tests_pak" ] if (is_android) { + sources += [ + "test/android/browsertests_apk/components_browser_tests_android.cc", + "test/android/browsertests_apk/components_browser_tests_android.h", + "test/android/browsertests_apk/components_browser_tests_jni_onload.cc", + ] sources -= [ "autofill/content/browser/risk/fingerprint_browsertest.cc" ] + deps += [ + ":components_browsertests_jni_headers", + "//testing/android/native_test:native_test_util", + ] + + use_launcher = false } if (is_linux) { @@ -379,6 +407,7 @@ test("components_perftests") { sources = [ + "scheduler/child/task_queue_manager_perftest.cc", "visitedlink/test/visitedlink_perftest.cc", ] @@ -389,6 +418,7 @@ "//base", "//base/test:test_support_perf", "//testing/gtest", + "//testing/perf", "//content/test:test_support", "//components/visitedlink/browser", ]
diff --git a/components/OWNERS b/components/OWNERS index 05b239f..296d144 100644 --- a/components/OWNERS +++ b/components/OWNERS
@@ -58,6 +58,9 @@ per-file devtools_bridge.gyp=mnaganov@chromium.org per-file devtools_bridge.gyp=serya@chromium.org +per-file devtools_discovery.gyp*=dgozman@chromium.org +per-file devtools_discovery.gyp*=pfeldman@chromium.org + per-file devtools_http_handler.gyp*=dgozman@chromium.org per-file devtools_http_handler.gyp*=pfeldman@chromium.org
diff --git a/components/autofill.gypi b/components/autofill.gypi index f3d2f6a..25f6909a 100644 --- a/components/autofill.gypi +++ b/components/autofill.gypi
@@ -203,12 +203,10 @@ 'autofill/core/browser/webdata/autofill_webdata_service_observer.h', ], 'conditions': [ - ['desktop_linux==1', { + ['desktop_linux != 1', { # Controls whether Wallet cards can be saved to the local instance of # chrome. - 'defines': [ 'ENABLE_SAVE_WALLET_CARDS_LOCALLY=0' ], - }, { - 'defines': [ 'ENABLE_SAVE_WALLET_CARDS_LOCALLY=1' ], + 'defines': [ 'ENABLE_SAVE_WALLET_CARDS_LOCALLY' ], }], ],
diff --git a/components/autofill/content/browser/wallet/wallet_signin_helper.cc b/components/autofill/content/browser/wallet/wallet_signin_helper.cc index fc9b66f..92a228d 100644 --- a/components/autofill/content/browser/wallet/wallet_signin_helper.cc +++ b/components/autofill/content/browser/wallet/wallet_signin_helper.cc
@@ -38,7 +38,7 @@ void GetGoogleCookiesCallback( const base::Callback<void(const std::string&)>& callback, const net::CookieList& cookies) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); // Cookies for parent domains will also be returned; we only want cookies with // exact host matches. TODO(estade): really? @@ -62,7 +62,7 @@ void GetGoogleCookies( scoped_refptr<net::URLRequestContextGetter> request_context_getter, const base::Callback<void(const std::string&)>& callback) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); net::URLRequestContext* url_request_context = request_context_getter->GetURLRequestContext(); @@ -166,7 +166,7 @@ void WalletSigninHelper::ReturnWalletCookieValue( const std::string& cookie_value) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); delegate_->OnDidFetchWalletCookieValue(cookie_value); }
diff --git a/components/autofill/content/renderer/password_form_conversion_utils_browsertest.cc b/components/autofill/content/renderer/password_form_conversion_utils_browsertest.cc index d4b3f45..6971997 100644 --- a/components/autofill/content/renderer/password_form_conversion_utils_browsertest.cc +++ b/components/autofill/content/renderer/password_form_conversion_utils_browsertest.cc
@@ -105,10 +105,19 @@ DISALLOW_COPY_AND_ASSIGN(PasswordFormBuilder); }; -class PasswordFormConversionUtilsTest : public content::RenderViewTest { +// RenderVIewTest-based tests crash on Android +// http://crbug.com/187500 +#if defined(OS_ANDROID) +#define MAYBE_PasswordFormConversionUtilsTest \ + DISABLED_PasswordFormConversionUtilsTest +#else +#define MAYBE_PasswordFormConversionUtilsTest PasswordFormConversionUtilsTest +#endif // defined(OS_ANDROID) + +class MAYBE_PasswordFormConversionUtilsTest : public content::RenderViewTest { public: - PasswordFormConversionUtilsTest() : content::RenderViewTest() {} - ~PasswordFormConversionUtilsTest() override {} + MAYBE_PasswordFormConversionUtilsTest() : content::RenderViewTest() {} + ~MAYBE_PasswordFormConversionUtilsTest() override {} protected: // Loads the given |html|, retrieves the sole WebFormElement from it, and then @@ -138,12 +147,12 @@ } private: - DISALLOW_COPY_AND_ASSIGN(PasswordFormConversionUtilsTest); + DISALLOW_COPY_AND_ASSIGN(MAYBE_PasswordFormConversionUtilsTest); }; } // namespace -TEST_F(PasswordFormConversionUtilsTest, BasicFormAttributes) { +TEST_F(MAYBE_PasswordFormConversionUtilsTest, BasicFormAttributes) { PasswordFormBuilder builder(kTestFormActionURL); builder.AddUsernameField("username", "johnsmith", NULL); builder.AddSubmitButton("inactive_submit", false); @@ -170,7 +179,7 @@ EXPECT_EQ(PasswordForm::TYPE_MANUAL, password_form->type); } -TEST_F(PasswordFormConversionUtilsTest, DisabledFieldsAreIgnored) { +TEST_F(MAYBE_PasswordFormConversionUtilsTest, DisabledFieldsAreIgnored) { PasswordFormBuilder builder(kTestFormActionURL); builder.AddUsernameField("username", "johnsmith", NULL); builder.AddDisabledUsernameField(); @@ -188,7 +197,7 @@ EXPECT_EQ(base::UTF8ToUTF16("secret"), password_form->password_value); } -TEST_F(PasswordFormConversionUtilsTest, IdentifyingUsernameFields) { +TEST_F(MAYBE_PasswordFormConversionUtilsTest, IdentifyingUsernameFields) { // Each test case consists of a set of parameters to be plugged into the // PasswordFormBuilder below, plus the corresponding expectations. struct TestCase { @@ -273,7 +282,7 @@ } } -TEST_F(PasswordFormConversionUtilsTest, IdentifyingTwoPasswordFields) { +TEST_F(MAYBE_PasswordFormConversionUtilsTest, IdentifyingTwoPasswordFields) { // Each test case consists of a set of parameters to be plugged into the // PasswordFormBuilder below, plus the corresponding expectations. struct TestCase { @@ -328,7 +337,7 @@ } } -TEST_F(PasswordFormConversionUtilsTest, IdentifyingThreePasswordFields) { +TEST_F(MAYBE_PasswordFormConversionUtilsTest, IdentifyingThreePasswordFields) { // Each test case consists of a set of parameters to be plugged into the // PasswordFormBuilder below, plus the corresponding expectations. struct TestCase { @@ -389,7 +398,7 @@ } } -TEST_F(PasswordFormConversionUtilsTest, +TEST_F(MAYBE_PasswordFormConversionUtilsTest, IdentifyingPasswordFieldsWithAutocompleteAttributes) { // Each test case consists of a set of parameters to be plugged into the // PasswordFormBuilder below, plus the corresponding expectations. @@ -527,7 +536,7 @@ } } -TEST_F(PasswordFormConversionUtilsTest, InvalidFormDueToBadActionURL) { +TEST_F(MAYBE_PasswordFormConversionUtilsTest, InvalidFormDueToBadActionURL) { PasswordFormBuilder builder("invalid_target"); builder.AddUsernameField("username", "JohnSmith", NULL); builder.AddSubmitButton("submit", true); @@ -539,7 +548,8 @@ EXPECT_FALSE(password_form); } -TEST_F(PasswordFormConversionUtilsTest, InvalidFormDueToNoPasswordFields) { +TEST_F(MAYBE_PasswordFormConversionUtilsTest, + InvalidFormDueToNoPasswordFields) { PasswordFormBuilder builder(kTestFormActionURL); builder.AddUsernameField("username1", "John", NULL); builder.AddUsernameField("username2", "Smith", NULL); @@ -551,7 +561,7 @@ EXPECT_FALSE(password_form); } -TEST_F(PasswordFormConversionUtilsTest, +TEST_F(MAYBE_PasswordFormConversionUtilsTest, InvalidFormsDueToConfusingPasswordFields) { // Each test case consists of a set of parameters to be plugged into the // PasswordFormBuilder below. @@ -582,7 +592,7 @@ } } -TEST_F(PasswordFormConversionUtilsTest, +TEST_F(MAYBE_PasswordFormConversionUtilsTest, InvalidFormDueToTooManyPasswordFieldsWithoutAutocompleteAttributes) { PasswordFormBuilder builder(kTestFormActionURL); builder.AddUsernameField("username1", "John", NULL); @@ -598,7 +608,7 @@ EXPECT_FALSE(password_form); } -TEST_F(PasswordFormConversionUtilsTest, LayoutClassificationLogin) { +TEST_F(MAYBE_PasswordFormConversionUtilsTest, LayoutClassificationLogin) { PasswordFormBuilder builder(kTestFormActionURL); builder.AddHiddenField(); builder.AddUsernameField("username", "", nullptr); @@ -612,7 +622,7 @@ EXPECT_EQ(PasswordForm::Layout::LAYOUT_OTHER, login_form->layout); } -TEST_F(PasswordFormConversionUtilsTest, LayoutClassificationSignup) { +TEST_F(MAYBE_PasswordFormConversionUtilsTest, LayoutClassificationSignup) { PasswordFormBuilder builder(kTestFormActionURL); builder.AddUsernameField("someotherfield", "", nullptr); builder.AddUsernameField("username", "", nullptr); @@ -628,7 +638,7 @@ EXPECT_EQ(PasswordForm::Layout::LAYOUT_OTHER, signup_form->layout); } -TEST_F(PasswordFormConversionUtilsTest, LayoutClassificationChange) { +TEST_F(MAYBE_PasswordFormConversionUtilsTest, LayoutClassificationChange) { PasswordFormBuilder builder(kTestFormActionURL); builder.AddUsernameField("username", "", nullptr); builder.AddPasswordField("old_password", "", nullptr); @@ -644,7 +654,8 @@ EXPECT_EQ(PasswordForm::Layout::LAYOUT_OTHER, change_form->layout); } -TEST_F(PasswordFormConversionUtilsTest, LayoutClassificationLoginPlusSignup_A) { +TEST_F(MAYBE_PasswordFormConversionUtilsTest, + LayoutClassificationLoginPlusSignup_A) { PasswordFormBuilder builder(kTestFormActionURL); builder.AddUsernameField("username", "", nullptr); builder.AddHiddenField(); @@ -665,7 +676,8 @@ login_plus_signup_form->layout); } -TEST_F(PasswordFormConversionUtilsTest, LayoutClassificationLoginPlusSignup_B) { +TEST_F(MAYBE_PasswordFormConversionUtilsTest, + LayoutClassificationLoginPlusSignup_B) { PasswordFormBuilder builder(kTestFormActionURL); builder.AddUsernameField("username", "", nullptr); builder.AddHiddenField();
diff --git a/components/autofill/core/browser/BUILD.gn b/components/autofill/core/browser/BUILD.gn index 17ca7fd..6c2d3d1 100644 --- a/components/autofill/core/browser/BUILD.gn +++ b/components/autofill/core/browser/BUILD.gn
@@ -120,10 +120,8 @@ ] # Controls whether Wallet cards can be saved to the local instance of chrome. - if (is_desktop_linux) { - defines = [ "ENABLE_SAVE_WALLET_CARDS_LOCALLY=0" ] - } else { - defines = [ "ENABLE_SAVE_WALLET_CARDS_LOCALLY=1" ] + if (!is_desktop_linux) { + defines = [ "ENABLE_SAVE_WALLET_CARDS_LOCALLY" ] } deps = [
diff --git a/components/autofill/core/browser/autofill_manager.cc b/components/autofill/core/browser/autofill_manager.cc index 8d26935..beb4281 100644 --- a/components/autofill/core/browser/autofill_manager.cc +++ b/components/autofill/core/browser/autofill_manager.cc
@@ -397,6 +397,8 @@ if (submitted_form->IsAutofillable()) ImportFormData(*submitted_form); + recently_unmasked_cards_.clear(); + return true; } @@ -765,7 +767,8 @@ void AutofillManager::OnLoadedServerPredictions( const std::string& response_xml) { // Parse and store the server predictions. - FormStructure::ParseQueryResponse(response_xml, form_structures_.get()); + FormStructure::ParseQueryResponse(response_xml, form_structures_.get(), + client_->GetRapporService()); // Forward form structures to the password generation manager to detect // account creation forms. @@ -799,6 +802,7 @@ if (!real_pan.empty()) { DCHECK_EQ(AutofillClient::SUCCESS, result); credit_card_form_event_logger_->OnDidFillSuggestion(unmasking_card_); + recently_unmasked_cards_.push_back(unmasking_card_); unmasking_card_.set_record_type(CreditCard::FULL_SERVER_CARD); unmasking_card_.SetNumber(base::UTF8ToUTF16(real_pan)); if (unmask_response_.should_store_pan) @@ -824,6 +828,17 @@ if (!personal_data_->ImportFormData(submitted_form, &imported_credit_card)) return; + // Don't offer to save any cards that were recently unmasked. + if (recently_unmasked_cards_.end() != + std::find_if(recently_unmasked_cards_.begin(), + recently_unmasked_cards_.end(), + [&imported_credit_card](const CreditCard& unmasked) -> bool { + return unmasked.TypeAndLastFourDigits() == + imported_credit_card->TypeAndLastFourDigits(); + })) { + return; + } + // If credit card information was submitted, we need to confirm whether to // save it. if (imported_credit_card) {
diff --git a/components/autofill/core/browser/autofill_manager.h b/components/autofill/core/browser/autofill_manager.h index 542e8b8e..f69634a 100644 --- a/components/autofill/core/browser/autofill_manager.h +++ b/components/autofill/core/browser/autofill_manager.h
@@ -419,6 +419,10 @@ // Time when we requested the last real pan base::Time real_pan_request_timestamp_; + // Masked copies of recently unmasked cards, to help avoid double-asking to + // save the card (in the prompt and in the infobar after submit). + std::vector<CreditCard> recently_unmasked_cards_; + // SuggestionBackendID to ID mapping. We keep two maps to convert back and // forth. These should be used only by BackendIDToInt and IntToBackendID. // Note that the integers are not frontend IDs. @@ -470,6 +474,7 @@ FormSubmittedAutocompleteEnabled); FRIEND_TEST_ALL_PREFIXES(AutofillManagerTest, AutocompleteOffRespectedForAutocomplete); + FRIEND_TEST_ALL_PREFIXES(AutofillManagerTest, DontOfferToSaveWalletCard); DISALLOW_COPY_AND_ASSIGN(AutofillManager); };
diff --git a/components/autofill/core/browser/autofill_manager_unittest.cc b/components/autofill/core/browser/autofill_manager_unittest.cc index 238b205d..d2255cb5 100644 --- a/components/autofill/core/browser/autofill_manager_unittest.cc +++ b/components/autofill/core/browser/autofill_manager_unittest.cc
@@ -35,6 +35,7 @@ #include "components/autofill/core/common/form_data.h" #include "components/autofill/core/common/form_field_data.h" #include "grit/components_strings.h" +#include "net/url_request/url_request_test_util.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/base/l10n/l10n_util.h" @@ -51,6 +52,19 @@ const int kDefaultPageID = 137; +class MockAutofillClient : public TestAutofillClient { + public: + MockAutofillClient() {} + + ~MockAutofillClient() override {} + + MOCK_METHOD1(ConfirmSaveCreditCard, + void(const base::Closure& save_card_callback)); + + private: + DISALLOW_COPY_AND_ASSIGN(MockAutofillClient); +}; + class TestPersonalDataManager : public PersonalDataManager { public: TestPersonalDataManager() @@ -627,6 +641,9 @@ personal_data_.set_database(autofill_client_.GetDatabase()); personal_data_.SetPrefService(autofill_client_.GetPrefs()); autofill_driver_.reset(new MockAutofillDriver()); + request_context_ = + new net::TestURLRequestContextGetter(base::MessageLoopProxy::current()); + autofill_driver_->SetURLRequestContext(request_context_.get()); autofill_manager_.reset(new TestAutofillManager( autofill_driver_.get(), &autofill_client_, &personal_data_)); @@ -646,6 +663,8 @@ // need to care about removing self as an observer in destruction. personal_data_.set_database(scoped_refptr<AutofillWebDataService>(NULL)); personal_data_.SetPrefService(NULL); + + request_context_ = nullptr; } void GetAutofillSuggestions(int query_id, @@ -714,10 +733,11 @@ protected: base::MessageLoop message_loop_; - TestAutofillClient autofill_client_; + MockAutofillClient autofill_client_; scoped_ptr<MockAutofillDriver> autofill_driver_; scoped_ptr<TestAutofillManager> autofill_manager_; scoped_ptr<TestAutofillExternalDelegate> external_delegate_; + scoped_refptr<net::TestURLRequestContextGetter> request_context_; TestPersonalDataManager personal_data_; }; @@ -3036,35 +3056,6 @@ } #endif // defined(OS_MACOSX) && !defined(OS_IOS) -namespace { - -class MockAutofillClient : public TestAutofillClient { - public: - MockAutofillClient() {} - - ~MockAutofillClient() override {} - - void ShowRequestAutocompleteDialog(const FormData& form, - content::RenderFrameHost* rfh, - const ResultCallback& callback) override { - callback.Run(user_supplied_data_ ? AutocompleteResultSuccess : - AutocompleteResultErrorDisabled, - base::string16(), - user_supplied_data_.get()); - } - - void SetUserSuppliedData(scoped_ptr<FormStructure> user_supplied_data) { - user_supplied_data_.reset(user_supplied_data.release()); - } - - private: - scoped_ptr<FormStructure> user_supplied_data_; - - DISALLOW_COPY_AND_ASSIGN(MockAutofillClient); -}; - -} // namespace - // Test our external delegate is called at the right time. TEST_F(AutofillManagerTest, TestExternalDelegate) { FormData form; @@ -3139,4 +3130,48 @@ "04/12", kVisaCard, autofill_manager_->GetPackedCreditCardID(4))); } +TEST_F(AutofillManagerTest, DontOfferToSaveWalletCard) { + // This line silences the warning from RealPanWalletClient about matching + // sync and wallet server types. + base::CommandLine::ForCurrentProcess()->AppendSwitchASCII( + "sync-url", "https://google.com"); + + // Set up our form data. + FormData form; + CreateTestCreditCardFormData(&form, true, false); + std::vector<FormData> forms(1, form); + FormsSeen(forms); + + CreditCard card(CreditCard::MASKED_SERVER_CARD, "a123"); + test::SetCreditCardInfo(&card, "John Dillinger", "1881" /* Visa */, "01", + "2017"); + card.SetTypeForMaskedCard(kVisaCard); + + EXPECT_CALL(autofill_client_, ConfirmSaveCreditCard(_)).Times(0); + EXPECT_CALL(*autofill_driver_, SendFormDataToRenderer(_, _, _)); + autofill_manager_->FillOrPreviewCreditCardForm( + AutofillDriver::FORM_DATA_ACTION_FILL, kDefaultPageID, form, + form.fields[0], card, 0); + + // Manually fill out |form| so we can use it in OnFormSubmitted. + for (size_t i = 0; i < form.fields.size(); ++i) { + if (form.fields[i].name == ASCIIToUTF16("cardnumber")) + form.fields[i].value = ASCIIToUTF16("4012888888881881"); + else if (form.fields[i].name == ASCIIToUTF16("nameoncard")) + form.fields[i].value = ASCIIToUTF16("John H Dillinger"); + else if (form.fields[i].name == ASCIIToUTF16("ccmonth")) + form.fields[i].value = ASCIIToUTF16("01"); + else if (form.fields[i].name == ASCIIToUTF16("ccyear")) + form.fields[i].value = ASCIIToUTF16("2017"); + } + + AutofillManager::UnmaskResponse response; + response.should_store_pan = false; + response.cvc = ASCIIToUTF16("123"); + autofill_manager_->OnUnmaskResponse(response); + autofill_manager_->OnDidGetRealPan(AutofillClient::SUCCESS, + "4012888888881881"); + autofill_manager_->OnFormSubmitted(form); +} + } // namespace autofill
diff --git a/components/autofill/core/browser/form_structure.cc b/components/autofill/core/browser/form_structure.cc index a582b0a..5c76c62 100644 --- a/components/autofill/core/browser/form_structure.cc +++ b/components/autofill/core/browser/form_structure.cc
@@ -550,9 +550,9 @@ } // static -void FormStructure::ParseQueryResponse( - const std::string& response_xml, - const std::vector<FormStructure*>& forms) { +void FormStructure::ParseQueryResponse(const std::string& response_xml, + const std::vector<FormStructure*>& forms, + rappor::RapporService* rappor_service) { AutofillMetrics::LogServerQueryMetric( AutofillMetrics::QUERY_RESPONSE_RECEIVED); @@ -579,6 +579,7 @@ FormStructure* form = *iter; form->upload_required_ = upload_required; + bool query_response_has_no_server_data = true; for (std::vector<AutofillField*>::iterator field = form->fields_.begin(); field != form->fields_.end(); ++field) { if (form->ShouldSkipField(**field)) @@ -589,6 +590,9 @@ if (current_info == field_infos.end()) break; + query_response_has_no_server_data &= + current_info->field_type == NO_SERVER_DATA; + // If |form->has_author_specified_types| only password fields should be // updated. if (!form->has_author_specified_types_ || @@ -612,6 +616,12 @@ ++current_info; } + if (query_response_has_no_server_data && form->source_url().is_valid()) { + rappor::SampleDomainAndRegistryFromGURL( + rappor_service, "Autofill.QueryResponseHasNoServerDataForForm", + form->source_url()); + } + form->UpdateAutofillCount(); form->IdentifySections(false); }
diff --git a/components/autofill/core/browser/form_structure.h b/components/autofill/core/browser/form_structure.h index 8e2e7a1..5a3f5065 100644 --- a/components/autofill/core/browser/form_structure.h +++ b/components/autofill/core/browser/form_structure.h
@@ -77,8 +77,10 @@ // Parses the field types from the server query response. |forms| must be the // same as the one passed to EncodeQueryRequest when constructing the query. + // |rappor_service| may be null. static void ParseQueryResponse(const std::string& response_xml, - const std::vector<FormStructure*>& forms); + const std::vector<FormStructure*>& forms, + rappor::RapporService* rappor_service); // Returns predictions using the details from the given |form_structures| and // their fields' predicted types.
diff --git a/components/autofill/core/browser/form_structure_unittest.cc b/components/autofill/core/browser/form_structure_unittest.cc index a437aa4..7d05b72 100644 --- a/components/autofill/core/browser/form_structure_unittest.cc +++ b/components/autofill/core/browser/form_structure_unittest.cc
@@ -11,10 +11,12 @@ #include "components/autofill/core/common/autofill_switches.h" #include "components/autofill/core/common/form_data.h" #include "components/autofill/core/common/form_field_data.h" +#include "components/rappor/test_rappor_service.h" #include "testing/gtest/include/gtest/gtest.h" #include "url/gurl.h" using base::ASCIIToUTF16; +using rappor::TestRapporService; namespace autofill { @@ -2379,7 +2381,9 @@ } TEST(FormStructureTest, ParseQueryResponse) { + TestRapporService rappor_service; FormData form; + form.origin = GURL("http://foo.com"); FormFieldData field; field.form_control_type = "text"; @@ -2420,7 +2424,7 @@ "<field autofilltype=\"0\" />" "</autofillqueryresponse>"; - FormStructure::ParseQueryResponse(response, forms.get()); + FormStructure::ParseQueryResponse(response, forms.get(), &rappor_service); ASSERT_GE(forms[0]->field_count(), 2U); ASSERT_GE(forms[1]->field_count(), 2U); @@ -2428,11 +2432,17 @@ EXPECT_EQ(30, forms[0]->field(1)->server_type()); EXPECT_EQ(9, forms[1]->field(0)->server_type()); EXPECT_EQ(0, forms[1]->field(1)->server_type()); + + // No RAPPOR metrics are logged in the case there is server data available for + // all forms. + EXPECT_EQ(0, rappor_service.GetReportsCount()); } // If user defined types are present, only parse password fields. TEST(FormStructureTest, ParseQueryResponseAuthorDefinedTypes) { + TestRapporService rappor_service; FormData form; + form.origin = GURL("http://foo.com"); FormFieldData field; field.label = ASCIIToUTF16("email"); @@ -2457,11 +2467,159 @@ "<field autofilltype=\"76\" />" "</autofillqueryresponse>"; - FormStructure::ParseQueryResponse(response, forms.get()); + FormStructure::ParseQueryResponse(response, forms.get(), &rappor_service); ASSERT_GE(forms[0]->field_count(), 2U); EXPECT_EQ(NO_SERVER_DATA, forms[0]->field(0)->server_type()); EXPECT_EQ(76, forms[0]->field(1)->server_type()); } +// If the server returns NO_SERVER_DATA for one of the forms, expect RAPPOR +// logging. +TEST(FormStructureTest, ParseQueryResponse_RapporLogging_OneFormNoServerData) { + TestRapporService rappor_service; + FormData form; + form.origin = GURL("http://foo.com"); + FormFieldData field; + field.form_control_type = "text"; + + field.label = ASCIIToUTF16("fullname"); + field.name = ASCIIToUTF16("fullname"); + form.fields.push_back(field); + + field.label = ASCIIToUTF16("address"); + field.name = ASCIIToUTF16("address"); + form.fields.push_back(field); + + ScopedVector<FormStructure> forms; + forms.push_back(new FormStructure(form)); + + field.label = ASCIIToUTF16("email"); + field.name = ASCIIToUTF16("email"); + form.fields.push_back(field); + + field.label = ASCIIToUTF16("password"); + field.name = ASCIIToUTF16("password"); + field.form_control_type = "password"; + form.fields.push_back(field); + + forms.push_back(new FormStructure(form)); + + std::string response = + "<autofillqueryresponse>" + "<field autofilltype=\"0\" />" + "<field autofilltype=\"0\" />" + "<field autofilltype=\"9\" />" + "<field autofilltype=\"0\" />" + "</autofillqueryresponse>"; + + FormStructure::ParseQueryResponse(response, forms.get(), &rappor_service); + + EXPECT_EQ(1, rappor_service.GetReportsCount()); + std::string sample; + rappor::RapporType type; + EXPECT_TRUE(rappor_service.GetRecordedSampleForMetric( + "Autofill.QueryResponseHasNoServerDataForForm", &sample, &type)); + EXPECT_EQ("foo.com", sample); + EXPECT_EQ(rappor::ETLD_PLUS_ONE_RAPPOR_TYPE, type); +} + +// If the server returns NO_SERVER_DATA for both of the forms, expect RAPPOR +// logging. +TEST(FormStructureTest, ParseQueryResponse_RapporLogging_AllFormsNoServerData) { + TestRapporService rappor_service; + FormData form; + form.origin = GURL("http://foo.com"); + FormFieldData field; + field.form_control_type = "text"; + + field.label = ASCIIToUTF16("fullname"); + field.name = ASCIIToUTF16("fullname"); + form.fields.push_back(field); + + field.label = ASCIIToUTF16("address"); + field.name = ASCIIToUTF16("address"); + form.fields.push_back(field); + + ScopedVector<FormStructure> forms; + forms.push_back(new FormStructure(form)); + + field.label = ASCIIToUTF16("email"); + field.name = ASCIIToUTF16("email"); + form.fields.push_back(field); + + field.label = ASCIIToUTF16("password"); + field.name = ASCIIToUTF16("password"); + field.form_control_type = "password"; + form.fields.push_back(field); + + forms.push_back(new FormStructure(form)); + + std::string response = + "<autofillqueryresponse>" + "<field autofilltype=\"0\" />" + "<field autofilltype=\"0\" />" + "<field autofilltype=\"0\" />" + "<field autofilltype=\"0\" />" + "</autofillqueryresponse>"; + + FormStructure::ParseQueryResponse(response, forms.get(), &rappor_service); + + // Even though both forms are logging to RAPPOR, there is only one sample for + // a given eTLD+1. + EXPECT_EQ(1, rappor_service.GetReportsCount()); + std::string sample; + rappor::RapporType type; + EXPECT_TRUE(rappor_service.GetRecordedSampleForMetric( + "Autofill.QueryResponseHasNoServerDataForForm", &sample, &type)); + EXPECT_EQ("foo.com", sample); + EXPECT_EQ(rappor::ETLD_PLUS_ONE_RAPPOR_TYPE, type); +} + +// If the server returns NO_SERVER_DATA for only some of the fields, expect no +// RAPPOR logging. +TEST(FormStructureTest, ParseQueryResponse_RapporLogging_PartialNoServerData) { + TestRapporService rappor_service; + FormData form; + form.origin = GURL("http://foo.com"); + FormFieldData field; + field.form_control_type = "text"; + + field.label = ASCIIToUTF16("fullname"); + field.name = ASCIIToUTF16("fullname"); + form.fields.push_back(field); + + field.label = ASCIIToUTF16("address"); + field.name = ASCIIToUTF16("address"); + form.fields.push_back(field); + + ScopedVector<FormStructure> forms; + forms.push_back(new FormStructure(form)); + + field.label = ASCIIToUTF16("email"); + field.name = ASCIIToUTF16("email"); + form.fields.push_back(field); + + field.label = ASCIIToUTF16("password"); + field.name = ASCIIToUTF16("password"); + field.form_control_type = "password"; + form.fields.push_back(field); + + forms.push_back(new FormStructure(form)); + + std::string response = + "<autofillqueryresponse>" + "<field autofilltype=\"0\" />" + "<field autofilltype=\"10\" />" + "<field autofilltype=\"0\" />" + "<field autofilltype=\"11\" />" + "</autofillqueryresponse>"; + + FormStructure::ParseQueryResponse(response, forms.get(), &rappor_service); + + // No RAPPOR metrics are logged in the case there is at least some server data + // available for all forms. + EXPECT_EQ(0, rappor_service.GetReportsCount()); +} + } // namespace autofill
diff --git a/components/autofill/core/browser/personal_data_manager.cc b/components/autofill/core/browser/personal_data_manager.cc index 15edaac..9af96ab 100644 --- a/components/autofill/core/browser/personal_data_manager.cc +++ b/components/autofill/core/browser/personal_data_manager.cc
@@ -490,8 +490,9 @@ } // Also don't offer to save if we already have this stored as a full wallet - // card. (In particular this comes up just after filling and submitting a - // Wallet card.) + // card. Note that we will offer to save masked server cards, as long as + // the user re-typed the info by hand. See AutofillManager's + // |recently_unmasked_cards_|. if (local_imported_credit_card) { for (CreditCard* card : server_credit_cards_) { if (card->record_type() == CreditCard::FULL_SERVER_CARD &&
diff --git a/components/autofill/core/browser/personal_data_manager_unittest.cc b/components/autofill/core/browser/personal_data_manager_unittest.cc index 90828085..5780908 100644 --- a/components/autofill/core/browser/personal_data_manager_unittest.cc +++ b/components/autofill/core/browser/personal_data_manager_unittest.cc
@@ -15,6 +15,7 @@ #include "base/prefs/pref_service.h" #include "base/strings/utf_string_conversions.h" #include "base/synchronization/waitable_event.h" +#include "components/autofill/core/browser/autofill_experiments.h" #include "components/autofill/core/browser/autofill_profile.h" #include "components/autofill/core/browser/autofill_test_utils.h" #include "components/autofill/core/browser/form_structure.h" @@ -494,6 +495,16 @@ base::MessageLoop::current()->Run(); ASSERT_EQ(3U, personal_data_->GetCreditCards().size()); + + if (!OfferStoreUnmaskedCards()) { + for (CreditCard* card : personal_data_->GetCreditCards()) { + EXPECT_EQ(CreditCard::MASKED_SERVER_CARD, card->record_type()); + } + // The rest of this test doesn't work if we're force-masking all unmasked + // cards. + return; + } + // The GUIDs will be different, so just compare the data. for (size_t i = 0; i < 3; ++i) EXPECT_EQ(0, server_cards[i].Compare(*personal_data_->GetCreditCards()[i])); @@ -917,8 +928,8 @@ FormStructure form_structure(form); form_structure.DetermineHeuristicTypes(); scoped_ptr<CreditCard> imported_credit_card; - EXPECT_TRUE(personal_data_->ImportFormData(form_structure, - &imported_credit_card)); + EXPECT_TRUE( + personal_data_->ImportFormData(form_structure, &imported_credit_card)); const std::vector<AutofillProfile*>& profiles = personal_data_->GetProfiles(); ASSERT_EQ(1U, profiles.size()); } @@ -3143,6 +3154,16 @@ base::MessageLoop::current()->Run(); ASSERT_EQ(3U, personal_data_->GetCreditCards().size()); + + if (!OfferStoreUnmaskedCards()) { + for (CreditCard* card : personal_data_->GetCreditCards()) { + EXPECT_EQ(CreditCard::MASKED_SERVER_CARD, card->record_type()); + } + // The rest of this test doesn't work if we're force-masking all unmasked + // cards. + return; + } + // The GUIDs will be different, so just compare the data. for (size_t i = 0; i < 3; ++i) EXPECT_EQ(0, server_cards[i].Compare(*personal_data_->GetCreditCards()[i])); @@ -3253,4 +3274,67 @@ EXPECT_TRUE(personal_data_->GetProfiles().empty()); } +TEST_F(PersonalDataManagerTest, DontDuplicateServerCard) { + EnableWalletCardImport(); + + std::vector<CreditCard> server_cards; + server_cards.push_back(CreditCard(CreditCard::MASKED_SERVER_CARD, "a123")); + test::SetCreditCardInfo(&server_cards.back(), "John Dillinger", + "1881" /* Visa */, "01", "2017"); + server_cards.back().SetTypeForMaskedCard(kVisaCard); + + server_cards.push_back(CreditCard(CreditCard::FULL_SERVER_CARD, "c789")); + test::SetCreditCardInfo(&server_cards.back(), "Clyde Barrow", + "347666888555" /* American Express */, "04", "2015"); + + test::SetServerCreditCards(autofill_table_, server_cards); + personal_data_->Refresh(); + EXPECT_CALL(personal_data_observer_, OnPersonalDataChanged()) + .WillOnce(QuitMainMessageLoop()); + base::MessageLoop::current()->Run(); + + // A valid credit card form. A user re-types one of their masked cards. + // We should offer to save. + FormData form1; + FormFieldData field; + test::CreateTestFormField("Name on card:", "name_on_card", "John Dillinger", + "text", &field); + form1.fields.push_back(field); + test::CreateTestFormField("Card Number:", "card_number", "4012888888881881", + "text", &field); + form1.fields.push_back(field); + test::CreateTestFormField("Exp Month:", "exp_month", "01", "text", &field); + form1.fields.push_back(field); + test::CreateTestFormField("Exp Year:", "exp_year", "2017", "text", &field); + form1.fields.push_back(field); + + FormStructure form_structure1(form1); + form_structure1.DetermineHeuristicTypes(); + scoped_ptr<CreditCard> imported_credit_card; + EXPECT_TRUE( + personal_data_->ImportFormData(form_structure1, &imported_credit_card)); + EXPECT_TRUE(imported_credit_card); + imported_credit_card.reset(); + + // A user re-types (or fills with) an unmasked card. Don't offer to save + // again. + FormData form2; + test::CreateTestFormField("Name on card:", "name_on_card", "Clyde Barrow", + "text", &field); + form2.fields.push_back(field); + test::CreateTestFormField("Card Number:", "card_number", "347666888555", + "text", &field); + form2.fields.push_back(field); + test::CreateTestFormField("Exp Month:", "exp_month", "04", "text", &field); + form2.fields.push_back(field); + test::CreateTestFormField("Exp Year:", "exp_year", "2015", "text", &field); + form2.fields.push_back(field); + + FormStructure form_structure2(form2); + form_structure2.DetermineHeuristicTypes(); + EXPECT_FALSE( + personal_data_->ImportFormData(form_structure2, &imported_credit_card)); + EXPECT_FALSE(imported_credit_card); +} + } // namespace autofill
diff --git a/components/cdm/renderer/android_key_systems.cc b/components/cdm/renderer/android_key_systems.cc index 8076a2a..28b45eb 100644 --- a/components/cdm/renderer/android_key_systems.cc +++ b/components/cdm/renderer/android_key_systems.cc
@@ -62,18 +62,18 @@ // We are using MediaDrm API on Android and we cannot guarantee that API // doesn't use persistent storage on the device. Therefore always set - // persistent state to EME_FEATURE_ALWAYS_ENABLED to err on the safe side. + // persistent state to EmeFeatureSupport::ALWAYS_ENABLED to err on the + // safe side. if (codecs != media::EME_CODEC_NONE) { AddWidevineWithCodecs( - WIDEVINE, - codecs, - max_audio_robustness, - max_video_robustness, - media::EME_SESSION_TYPE_NOT_SUPPORTED, // persistent-license. - media::EME_SESSION_TYPE_NOT_SUPPORTED, // persistent-release-message. - media::EME_FEATURE_ALWAYS_ENABLED, // Persistent state. - media::EME_FEATURE_ALWAYS_ENABLED, // Distinctive identifier. + WIDEVINE, codecs, max_audio_robustness, max_video_robustness, + media::EmeSessionTypeSupport::NOT_SUPPORTED, // persistent-license. + media::EmeSessionTypeSupport:: + NOT_SUPPORTED, // persistent-release-message. + media::EmeFeatureSupport::ALWAYS_ENABLED, // Persistent state. + media::EmeFeatureSupport::ALWAYS_ENABLED, // Distinctive + // identifier. concrete_key_systems); } @@ -84,14 +84,15 @@ // TODO(ddorwin): Remove with unprefixed. http://crbug.com/249976 if (response.non_compositing_codecs != media::EME_CODEC_NONE) { AddWidevineWithCodecs( - WIDEVINE_HR_NON_COMPOSITING, - response.non_compositing_codecs, - EmeRobustness::HW_SECURE_CRYPTO, // Max audio robustness. - EmeRobustness::HW_SECURE_ALL, // Max video robustness. - media::EME_SESSION_TYPE_NOT_SUPPORTED, // persistent-license. - media::EME_SESSION_TYPE_NOT_SUPPORTED, // persistent-release-message. - media::EME_FEATURE_ALWAYS_ENABLED, // Persistent state. - media::EME_FEATURE_ALWAYS_ENABLED, // Distinctive identifier. + WIDEVINE_HR_NON_COMPOSITING, response.non_compositing_codecs, + EmeRobustness::HW_SECURE_CRYPTO, // Max audio robustness. + EmeRobustness::HW_SECURE_ALL, // Max video robustness. + media::EmeSessionTypeSupport::NOT_SUPPORTED, // persistent-license. + media::EmeSessionTypeSupport:: + NOT_SUPPORTED, // persistent-release-message. + media::EmeFeatureSupport::ALWAYS_ENABLED, // Persistent state. + media::EmeFeatureSupport::ALWAYS_ENABLED, // Distinctive + // identifier. concrete_key_systems); } } @@ -121,11 +122,13 @@ info.max_audio_robustness = EmeRobustness::EMPTY; info.max_video_robustness = EmeRobustness::EMPTY; // Assume the worst case (from a user point of view). - info.persistent_license_support = media::EME_SESSION_TYPE_NOT_SUPPORTED; + info.persistent_license_support = + media::EmeSessionTypeSupport::NOT_SUPPORTED; info.persistent_release_message_support = - media::EME_SESSION_TYPE_NOT_SUPPORTED; - info.persistent_state_support = media::EME_FEATURE_ALWAYS_ENABLED; - info.distinctive_identifier_support = media::EME_FEATURE_ALWAYS_ENABLED; + media::EmeSessionTypeSupport::NOT_SUPPORTED; + info.persistent_state_support = media::EmeFeatureSupport::ALWAYS_ENABLED; + info.distinctive_identifier_support = + media::EmeFeatureSupport::ALWAYS_ENABLED; concrete_key_systems->push_back(info); } }
diff --git a/components/components.gyp b/components/components.gyp index 9327c97..3f6eb49 100644 --- a/components/components.gyp +++ b/components/components.gyp
@@ -88,6 +88,7 @@ 'app_modal.gypi', 'browsing_data.gypi', 'cdm.gypi', + 'devtools_discovery.gypi', 'devtools_http_handler.gypi', 'navigation_interception.gypi', 'power.gypi',
diff --git a/components/components_browsertests.isolate b/components/components_browsertests.isolate index 2dff53ae..e60cea1 100644 --- a/components/components_browsertests.isolate +++ b/components/components_browsertests.isolate
@@ -34,18 +34,32 @@ ], }, }], + ['OS=="android"', { + 'variables': { + 'files': [ + '<(PRODUCT_DIR)/components_browsertests_apk_shell/assets/components_tests_resources.pak', + '<(PRODUCT_DIR)/components_browsertests_apk_shell/assets/content_shell.pak', + ], + }, + }], ['OS=="linux" or OS=="mac" or OS=="win"', { 'variables': { 'files': [ - 'test/data/', '../testing/test_env.py', - '../third_party/dom_distiller_js/dist/test/data/', '<(PRODUCT_DIR)/components_browsertests<(EXECUTABLE_SUFFIX)', '<(PRODUCT_DIR)/components_tests_resources.pak', '<(PRODUCT_DIR)/content_shell.pak', ], }, }], + ['OS=="android" or OS=="linux" or OS=="mac" or OS=="win"', { + 'variables': { + 'files': [ + 'test/data/', + '../third_party/dom_distiller_js/dist/test/data/', + ], + }, + }], ['OS=="linux"', { 'variables': { 'files': [
diff --git a/components/components_tests.gyp b/components/components_tests.gyp index 14710fa..f48690f 100644 --- a/components/components_tests.gyp +++ b/components/components_tests.gyp
@@ -430,6 +430,18 @@ 'rappor/rappor_service_unittest.cc', 'rappor/rappor_utils_unittest.cc', ], + 'scheduler_unittest_sources': [ + 'scheduler/child/nestable_task_runner_for_test.cc', + 'scheduler/child/nestable_task_runner_for_test.h', + 'scheduler/child/prioritizing_task_queue_selector_unittest.cc', + 'scheduler/child/scheduler_helper_unittest.cc', + 'scheduler/child/task_queue_manager_unittest.cc', + 'scheduler/child/test_time_source.cc', + 'scheduler/child/test_time_source.h', + 'scheduler/child/worker_scheduler_impl_unittest.cc', + 'scheduler/renderer/deadline_task_runner_unittest.cc', + 'scheduler/renderer/renderer_scheduler_impl_unittest.cc', + ], 'search_unittest_sources': [ 'search/search_android_unittest.cc', 'search/search_unittest.cc', @@ -815,6 +827,7 @@ '<@(navigation_interception_unittest_sources)', '<@(network_hints_unittest_sources)', '<@(power_unittest_sources)', + '<@(scheduler_unittest_sources)', '<@(storage_monitor_unittest_sources)', '<@(ui_unittest_sources)', '<@(visitedlink_unittest_sources)', @@ -849,6 +862,7 @@ 'components.gyp:web_cache_browser', 'components.gyp:web_modal', 'components.gyp:web_modal_test_support', + 'scheduler/scheduler.gyp:scheduler', 'webcrypto/webcrypto.gyp:webcrypto', '../third_party/re2/re2.gyp:re2', ], @@ -1109,6 +1123,87 @@ }, ], 'conditions': [ + ['OS == "android"', { + 'variables': { + 'components_browsertests_pak_input_resources': [ + '<(PRODUCT_DIR)/components_tests_resources.pak', + '<(PRODUCT_DIR)/content_shell/assets/content_shell.pak', + ], + 'conditions': [ + ['icu_use_data_file_flag==1', { + 'components_browsertests_pak_input_resources': [ + '<(PRODUCT_DIR)/icudtl.dat', + ], + }], + ['v8_use_external_startup_data==1', { + 'components_browsertests_pak_input_resources': [ + '<(PRODUCT_DIR)/natives_blob.bin', + '<(PRODUCT_DIR)/snapshot_blob.bin', + ], + }], + ], + }, + 'targets': [ + { + 'target_name': 'components_browsertests_paks_copy', + 'type': 'none', + 'dependencies': [ + 'components_browsertests', + ], + 'copies': [ + { + 'destination': '<(PRODUCT_DIR)/components_browsertests_apk_shell/assets', + 'files': [ + '<@(components_browsertests_pak_input_resources)', + ], + } + ], + }, + { + 'target_name': 'components_browsertests_manifest', + 'type': 'none', + 'variables': { + 'jinja_inputs': ['test/android/browsertests_apk/AndroidManifest.xml.jinja2'], + 'jinja_output': '<(SHARED_INTERMEDIATE_DIR)/components_browsertests_manifest/AndroidManifest.xml', + }, + 'includes': [ '../build/android/jinja_template.gypi' ], + }, + { + 'target_name': 'components_browsertests_jni_headers', + 'type': 'none', + 'sources': [ + 'test/android/browsertests_apk/src/org/chromium/components_browsertests_apk/ComponentsBrowserTestsActivity.java', + ], + 'variables': { + 'jni_gen_package': 'content/shell', + }, + 'includes': [ '../build/jni_generator.gypi' ], + }, + { + # TODO(GN) + 'target_name': 'components_browsertests_apk', + 'type': 'none', + 'dependencies': [ + '../content/content.gyp:content_icudata', + '../content/content.gyp:content_java', + '../content/content.gyp:content_v8_external_data', + '../content/content_shell_and_tests.gyp:content_java_test_support', + '../content/content_shell_and_tests.gyp:content_shell_java', + 'components_browsertests_paks_copy', + 'components_browsertests', + ], + 'variables': { + 'apk_name': 'components_browsertests', + 'java_in_dir': 'test/android/browsertests_apk', + 'android_manifest_path': '<(SHARED_INTERMEDIATE_DIR)/components_browsertests_manifest/AndroidManifest.xml', + 'resource_dir': 'test/android/browsertests_apk/res', + 'native_lib_target': 'libcomponents_browsertests', + 'asset_location': '<(PRODUCT_DIR)/components_browsertests_apk_shell/assets', + }, + 'includes': [ '../build/java_apk.gypi' ], + }, + ], + }], ['OS != "ios"', { 'targets': [ { @@ -1120,12 +1215,15 @@ '../base/base.gyp:test_support_perf', '../content/content_shell_and_tests.gyp:test_support_content', '../testing/gtest.gyp:gtest', + '../testing/perf/perf_test.gyp:perf_test', 'components.gyp:visitedlink_browser', + 'scheduler/scheduler.gyp:scheduler', ], 'include_dirs': [ '..', ], 'sources': [ + 'scheduler/child/task_queue_manager_perftest.cc', 'visitedlink/test/visitedlink_perftest.cc', ], 'conditions': [ @@ -1187,9 +1285,18 @@ ], 'conditions': [ ['OS == "android"', { + 'sources' : [ + 'test/android/browsertests_apk/components_browser_tests_android.cc', + 'test/android/browsertests_apk/components_browser_tests_android.h', + 'test/android/browsertests_apk/components_browser_tests_jni_onload.cc', + ], 'sources!': [ 'autofill/content/browser/risk/fingerprint_browsertest.cc', ], + 'dependencies': [ + '../testing/android/native_test.gyp:native_test_util', + 'components_browsertests_jni_headers', + ], }], ['OS == "linux"', { 'sources': [
diff --git a/components/content_settings/core/browser/host_content_settings_map.cc b/components/content_settings/core/browser/host_content_settings_map.cc index 74076c5..39f70ee 100644 --- a/components/content_settings/core/browser/host_content_settings_map.cc +++ b/components/content_settings/core/browser/host_content_settings_map.cc
@@ -493,6 +493,14 @@ return false; } #endif + + // Don't support ALLOW for the default media settings. + if ((content_type == CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA || + content_type == CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC) && + setting == CONTENT_SETTING_ALLOW) { + return false; + } + return IsSettingAllowedForType(prefs, setting, content_type); }
diff --git a/components/crash/browser/crash_dump_manager_android.cc b/components/crash/browser/crash_dump_manager_android.cc index 4814afdc..a6df1d92 100644 --- a/components/crash/browser/crash_dump_manager_android.cc +++ b/components/crash/browser/crash_dump_manager_android.cc
@@ -57,7 +57,7 @@ } base::File CrashDumpManager::CreateMinidumpFile(int child_process_id) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::PROCESS_LAUNCHER)); + DCHECK_CURRENTLY_ON(BrowserThread::PROCESS_LAUNCHER); base::FilePath minidump_path; if (!base::CreateTemporaryFile(&minidump_path)) return base::File();
diff --git a/components/cronet/android/cronet_url_request_adapter.cc b/components/cronet/android/cronet_url_request_adapter.cc index 64713bf..cec7756 100644 --- a/components/cronet/android/cronet_url_request_adapter.cc +++ b/components/cronet/android/cronet_url_request_adapter.cc
@@ -52,9 +52,11 @@ return reinterpret_cast<jlong>(adapter); } -// IOBuffer subclass for a buffer owned by a Java ByteBuffer. Keeps the -// ByteBuffer alive until destroyed. -class CronetURLRequestAdapter::IOBufferWithByteBuffer : public net::IOBuffer { +// net::WrappedIOBuffer subclass for a buffer owned by a Java ByteBuffer. Keeps +// the ByteBuffer alive until destroyed. Uses WrappedIOBuffer because data() is +// owned by the embedder. +class CronetURLRequestAdapter::IOBufferWithByteBuffer + : public net::WrappedIOBuffer { public: // Creates a buffer wrapping the Java ByteBuffer |jbyte_buffer|. |data| points // to the memory backed by the ByteBuffer, and position is the location to @@ -64,7 +66,7 @@ jobject jbyte_buffer, void* data, int position) - : net::IOBuffer(static_cast<char*>(data) + position), + : net::WrappedIOBuffer(static_cast<char*>(data) + position), initial_position_(position) { DCHECK(data); DCHECK_EQ(env->GetDirectBufferAddress(jbyte_buffer), data);
diff --git a/components/cronet/android/java/src/org/chromium/net/ChromiumUrlRequestContext.java b/components/cronet/android/java/src/org/chromium/net/ChromiumUrlRequestContext.java index f0207ec..03246f4 100644 --- a/components/cronet/android/java/src/org/chromium/net/ChromiumUrlRequestContext.java +++ b/components/cronet/android/java/src/org/chromium/net/ChromiumUrlRequestContext.java
@@ -77,8 +77,8 @@ } /** - * Starts NetLog logging to a file. The NetLog log level used is - * LOG_ALL_BUT_BYTES. + * Starts NetLog logging to a file. The NetLog capture mode is + * NetLogCaptureMode::Default(). * @param fileName The complete file path. It must not be empty. If file * exists, it is truncated before starting. If actively logging, * this method is ignored.
diff --git a/components/cronet/android/java/src/org/chromium/net/UrlRequestContext.java b/components/cronet/android/java/src/org/chromium/net/UrlRequestContext.java index 54eee3f..cddd64d 100644 --- a/components/cronet/android/java/src/org/chromium/net/UrlRequestContext.java +++ b/components/cronet/android/java/src/org/chromium/net/UrlRequestContext.java
@@ -56,8 +56,8 @@ public abstract void shutdown(); /** - * Starts NetLog logging to a file. The NetLog log level used is - * LOG_ALL_BUT_BYTES. + * Starts NetLog logging to a file. The NetLog capture mode used is + * NetLogCaptureMode::Default(). * @param fileName The complete file path. It must not be empty. If file * exists, it is truncated before starting. If actively logging, * this method is ignored.
diff --git a/components/cronet/android/java/src/org/chromium/net/urlconnection/CronetFixedModeOutputStream.java b/components/cronet/android/java/src/org/chromium/net/urlconnection/CronetFixedModeOutputStream.java index 3e4bb25..8b9519e 100644 --- a/components/cronet/android/java/src/org/chromium/net/urlconnection/CronetFixedModeOutputStream.java +++ b/components/cronet/android/java/src/org/chromium/net/urlconnection/CronetFixedModeOutputStream.java
@@ -151,7 +151,7 @@ // Reuse this buffer. mBuffer.clear(); // Quit message loop so embedder can write more data. - mMessageLoop.postQuitTask(); + mMessageLoop.quit(); } uploadDataSink.onReadSucceeded(false); }
diff --git a/components/cronet/android/java/src/org/chromium/net/urlconnection/CronetHttpURLConnection.java b/components/cronet/android/java/src/org/chromium/net/urlconnection/CronetHttpURLConnection.java index bed68b04..c1123d6d 100644 --- a/components/cronet/android/java/src/org/chromium/net/urlconnection/CronetHttpURLConnection.java +++ b/components/cronet/android/java/src/org/chromium/net/urlconnection/CronetHttpURLConnection.java
@@ -426,14 +426,14 @@ public void onResponseStarted(UrlRequest request, ResponseInfo info) { mResponseInfo = info; // Quits the message loop since we have the headers now. - mMessageLoop.postQuitTask(); + mMessageLoop.quit(); } @Override public void onReadCompleted(UrlRequest request, ResponseInfo info, ByteBuffer byteBuffer) { mResponseInfo = info; - mMessageLoop.postQuitTask(); + mMessageLoop.quit(); } @Override @@ -480,7 +480,7 @@ if (mInputStream != null) { mInputStream.setResponseDataCompleted(); } - mMessageLoop.postQuitTask(); + mMessageLoop.quit(); } }
diff --git a/components/cronet/android/java/src/org/chromium/net/urlconnection/MessageLoop.java b/components/cronet/android/java/src/org/chromium/net/urlconnection/MessageLoop.java index 9bad57b..f3d376c 100644 --- a/components/cronet/android/java/src/org/chromium/net/urlconnection/MessageLoop.java +++ b/components/cronet/android/java/src/org/chromium/net/urlconnection/MessageLoop.java
@@ -16,9 +16,6 @@ class MessageLoop implements Executor { private final BlockingQueue<Runnable> mQueue; - // A reusable runnable to quit the message loop. - private final Runnable mQuitTask; - // Indicates whether this message loop is currently running. private boolean mLoopRunning = false; @@ -28,23 +25,30 @@ // task enqueued. private boolean mLoopFailed = false; + // Used when assertions are enabled to enforce single-threaded use. + private static final long INVALID_THREAD_ID = -1; + private long mThreadId = INVALID_THREAD_ID; + MessageLoop() { mQueue = new LinkedBlockingQueue<Runnable>(); - mQuitTask = new Runnable() { - @Override - public void run() { - mLoopRunning = false; - } - }; + } + + private boolean calledOnValidThread() { + if (mThreadId == INVALID_THREAD_ID) { + mThreadId = Thread.currentThread().getId(); + return true; + } + return mThreadId == Thread.currentThread().getId(); } /** - * Runs the message loop. Be sure to call {@link MessageLoop#postQuitTask()} + * Runs the message loop. Be sure to call {@link MessageLoop#quit()} * to end the loop. If an interruptedException occurs, the loop cannot be * started again (see {@link #mLoopFailed}). * @throws IOException */ public void loop() throws IOException { + assert calledOnValidThread(); if (mLoopFailed) { throw new IllegalStateException( "Cannot run loop as an exception has occurred previously."); @@ -71,12 +75,13 @@ } /** - * Posts a reusable runnable, {@link #mQuitTask} to quit the loop. This - * causes the {@link #loop()} to stop after processing all currently - * enqueued messages. + * This causes {@link #loop()} to stop executing messages after the current + * message being executed. Should only be called from the currently + * executing message. */ - public void postQuitTask() { - execute(mQuitTask); + public void quit() { + assert calledOnValidThread(); + mLoopRunning = false; } /**
diff --git a/components/cronet/android/test/assets/test/content_length_mismatch.html b/components/cronet/android/test/assets/test/content_length_mismatch.html new file mode 100644 index 0000000..f883f8f --- /dev/null +++ b/components/cronet/android/test/assets/test/content_length_mismatch.html
@@ -0,0 +1 @@ +Response that lies about content length. \ No newline at end of file
diff --git a/components/cronet/android/test/assets/test/content_length_mismatch.html.mock-http-headers b/components/cronet/android/test/assets/test/content_length_mismatch.html.mock-http-headers new file mode 100644 index 0000000..b10b621 --- /dev/null +++ b/components/cronet/android/test/assets/test/content_length_mismatch.html.mock-http-headers
@@ -0,0 +1,3 @@ +HTTP/1.1 200 OK +Content-Type: text/html +Content-Length: 1000000
diff --git a/components/cronet/android/test/javatests/src/org/chromium/net/CronetTestBase.java b/components/cronet/android/test/javatests/src/org/chromium/net/CronetTestBase.java index 83a3b0f..7bd5982 100644 --- a/components/cronet/android/test/javatests/src/org/chromium/net/CronetTestBase.java +++ b/components/cronet/android/test/javatests/src/org/chromium/net/CronetTestBase.java
@@ -8,7 +8,6 @@ import android.content.Intent; import android.net.Uri; import android.test.ActivityInstrumentationTestCase2; -import android.text.TextUtils; import static org.chromium.base.test.util.ScalableTimeout.scaleTimeout; @@ -77,7 +76,7 @@ // Make sure the activity was created as expected. assertNotNull(getActivity()); try { - waitForActiveShellToBeDoneLoading(); + waitForActivityToBeDoneLoading(); } catch (Throwable e) { fail("Test activity has failed to load."); } @@ -94,41 +93,26 @@ } /** - * Waits for the Active shell to finish loading. This times out after - * WAIT_FOR_ACTIVE_SHELL_LOADING_TIMEOUT milliseconds and it shouldn't be - * used for long loading pages. Instead it should be used more for test - * initialization. The proper way to wait is to use a - * TestCallbackHelperContainer after the initial load is completed. + * Waits for the Activity to finish loading. This times out after + * WAIT_FOR_ACTIVE_SHELL_LOADING_TIMEOUT milliseconds. * * @return Whether or not the Shell was actually finished loading. * @throws InterruptedException */ - private boolean waitForActiveShellToBeDoneLoading() + private boolean waitForActivityToBeDoneLoading() throws InterruptedException { final CronetTestActivity activity = getActivity(); - // Wait for the Content Shell to be initialized. + // Wait for the Activity to load. return CriteriaHelper.pollForCriteria(new Criteria() { - @Override + @Override public boolean isSatisfied() { try { final AtomicBoolean isLoaded = new AtomicBoolean(false); runTestOnUiThread(new Runnable() { - @Override + @Override public void run() { - if (activity != null) { - // There are two cases here that need to be - // accounted for. - // The first is that we've just created a Shell - // and it isn't - // loading because it has no URL set yet. The - // second is that - // we've set a URL and it actually is loading. - isLoaded.set(!activity.isLoading() && !TextUtils - .isEmpty(activity.getUrl())); - } else { - isLoaded.set(false); - } + isLoaded.set(activity != null && !activity.isLoading()); } });
diff --git a/components/cronet/android/test/javatests/src/org/chromium/net/CronetUrlRequestTest.java b/components/cronet/android/test/javatests/src/org/chromium/net/CronetUrlRequestTest.java index 87c6499..e9aa7219 100644 --- a/components/cronet/android/test/javatests/src/org/chromium/net/CronetUrlRequestTest.java +++ b/components/cronet/android/test/javatests/src/org/chromium/net/CronetUrlRequestTest.java
@@ -191,6 +191,32 @@ assertEquals(listener.mResponseStep, ResponseStep.ON_SUCCEEDED); } + // Checks that UrlRequestListener.onFailed is only called once in the case + // of ERR_CONTENT_LENGTH_MISMATCH, which has an unusual failure path. + // See http://crbug.com/468803. + @SmallTest + @Feature({"Cronet"}) + public void testContentLengthMismatchFailsOnce() throws Exception { + String url = NativeTestServer.getFileURL( + "/content_length_mismatch.html"); + TestUrlRequestListener listener = startAndWaitForComplete(url); + assertEquals(200, listener.mResponseInfo.getHttpStatusCode()); + // The entire response body will be read before the error is returned. + // This is because the network stack returns data as it's read from the + // socket, and the socket close message which tiggers the error will + // only be passed along after all data has been read. + assertEquals("Response that lies about content length.", + listener.mResponseAsString); + assertNotNull(listener.mError); + assertEquals( + "Exception in CronetUrlRequest: net::ERR_CONTENT_LENGTH_MISMATCH", + listener.mError.getMessage()); + // Wait for a couple round trips to make sure there are no pending + // onFailed messages. This test relies on checks in + // TestUrlRequestListener catching a second onFailed call. + testSimpleGet(); + } + @SmallTest @Feature({"Cronet"}) public void testSetHttpMethod() throws Exception {
diff --git a/components/cronet/android/test/javatests/src/org/chromium/net/TestUrlRequestListener.java b/components/cronet/android/test/javatests/src/org/chromium/net/TestUrlRequestListener.java index cd4363e..db9583f3 100644 --- a/components/cronet/android/test/javatests/src/org/chromium/net/TestUrlRequestListener.java +++ b/components/cronet/android/test/javatests/src/org/chromium/net/TestUrlRequestListener.java
@@ -7,6 +7,7 @@ import android.os.ConditionVariable; import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertNull; import static junit.framework.Assert.assertTrue; @@ -172,7 +173,7 @@ assertEquals(mExecutorThread, Thread.currentThread()); assertTrue(mResponseStep == ResponseStep.ON_RESPONSE_STARTED || mResponseStep == ResponseStep.ON_READ_COMPLETED); - assertTrue(mError == null); + assertNull(mError); mResponseStep = ResponseStep.ON_SUCCEEDED; mExtendedResponseInfo = info; @@ -187,6 +188,9 @@ assertEquals(mExecutorThread, Thread.currentThread()); // Shouldn't happen after success. assertTrue(mResponseStep != ResponseStep.ON_SUCCEEDED); + // Should happen at most once for a single request. + assertFalse(mOnErrorCalled); + assertNull(mError); mOnErrorCalled = true; mError = error;
diff --git a/components/cronet/android/test/javatests/src/org/chromium/net/urlconnection/CronetBufferedOutputStreamTest.java b/components/cronet/android/test/javatests/src/org/chromium/net/urlconnection/CronetBufferedOutputStreamTest.java index d0d1a8c..698be48 100644 --- a/components/cronet/android/test/javatests/src/org/chromium/net/urlconnection/CronetBufferedOutputStreamTest.java +++ b/components/cronet/android/test/javatests/src/org/chromium/net/urlconnection/CronetBufferedOutputStreamTest.java
@@ -353,6 +353,8 @@ try { // Write remaining bytes. out.write(UPLOAD_DATA, 3, UPLOAD_DATA.length - 3); + // On Lollipop, default implementation only triggers the error when reading response. + connection.getInputStream(); fail(); } catch (ProtocolException e) { assertEquals("exceeded content-length limit of " @@ -381,6 +383,8 @@ for (int i = 0; i < UPLOAD_DATA.length; i++) { out.write(UPLOAD_DATA[i]); } + // On Lollipop, default implementation only triggers the error when reading response. + connection.getInputStream(); fail(); } catch (java.net.ProtocolException e) { assertEquals("exceeded content-length limit of "
diff --git a/components/cronet/android/test/javatests/src/org/chromium/net/urlconnection/CronetFixedModeOutputStreamTest.java b/components/cronet/android/test/javatests/src/org/chromium/net/urlconnection/CronetFixedModeOutputStreamTest.java index c2b7e45..be00a662 100644 --- a/components/cronet/android/test/javatests/src/org/chromium/net/urlconnection/CronetFixedModeOutputStreamTest.java +++ b/components/cronet/android/test/javatests/src/org/chromium/net/urlconnection/CronetFixedModeOutputStreamTest.java
@@ -133,6 +133,8 @@ OutputStream out = connection.getOutputStream(); try { out.write(UPLOAD_DATA); + // On Lollipop, default implementation only triggers the error when reading response. + connection.getInputStream(); fail(); } catch (ProtocolException e) { // Expected. @@ -162,10 +164,16 @@ try { // Try upload an extra byte. out.write(UPLOAD_DATA[UPLOAD_DATA.length - 1]); + // On Lollipop, default implementation only triggers the error when reading response. + connection.getInputStream(); fail(); } catch (ProtocolException e) { // Expected. - assertEquals("expected 0 bytes but received 1", e.getMessage()); + String expectedVariant = "expected 0 bytes but received 1"; + String expectedVariantOnLollipop = "expected " + (UPLOAD_DATA.length - 1) + + " bytes but received " + UPLOAD_DATA.length; + assertTrue(expectedVariant.equals(e.getMessage()) + || expectedVariantOnLollipop.equals(e.getMessage())); } connection.disconnect(); }
diff --git a/components/cronet/android/test/javatests/src/org/chromium/net/urlconnection/MessageLoopTest.java b/components/cronet/android/test/javatests/src/org/chromium/net/urlconnection/MessageLoopTest.java index b2e5fd9..b480967 100644 --- a/components/cronet/android/test/javatests/src/org/chromium/net/urlconnection/MessageLoopTest.java +++ b/components/cronet/android/test/javatests/src/org/chromium/net/urlconnection/MessageLoopTest.java
@@ -10,18 +10,32 @@ import org.chromium.net.CronetTestBase; import java.io.IOException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.ThreadFactory; /** * Tests the MessageLoop implementation. */ public class MessageLoopTest extends CronetTestBase { + private Thread mTestThread; + private final ExecutorService mExecutorService = + Executors.newSingleThreadExecutor(new ExecutorThreadFactory()); + private class ExecutorThreadFactory implements ThreadFactory { + public Thread newThread(Runnable r) { + mTestThread = new Thread(r); + return mTestThread; + } + } + private boolean mFailed = false; @SmallTest @Feature({"Cronet"}) public void testInterrupt() throws Exception { final MessageLoop loop = new MessageLoop(); assertFalse(loop.isRunning()); - TestThread thread = new TestThread() { + Future future = mExecutorService.submit(new Runnable() { @Override public void run() { try { @@ -31,23 +45,29 @@ // Expected interrupt. } } - }; - thread.start(); + }); Thread.sleep(1000); assertTrue(loop.isRunning()); assertFalse(loop.hasLoopFailed()); - thread.interrupt(); - Thread.sleep(1000); + mTestThread.interrupt(); + future.get(); assertFalse(loop.isRunning()); assertTrue(loop.hasLoopFailed()); - assertFalse(thread.mFailed); + assertFalse(mFailed); // Re-spinning the message loop is not allowed after interrupt. - try { - loop.loop(); - fail(); - } catch (IllegalStateException e) { - // Expected. - } + mExecutorService.submit(new Runnable() { + @Override + public void run() { + try { + loop.loop(); + fail(); + } catch (Exception e) { + if (!(e instanceof IllegalStateException)) { + fail(); + } + } + } + }).get(); } @SmallTest @@ -55,7 +75,7 @@ public void testTaskFailed() throws Exception { final MessageLoop loop = new MessageLoop(); assertFalse(loop.isRunning()); - TestThread thread = new TestThread() { + Future future = mExecutorService.submit(new Runnable() { @Override public void run() { try { @@ -67,44 +87,34 @@ } } } - }; + }); Runnable failedTask = new Runnable() { @Override public void run() { throw new NullPointerException(); } }; - thread.start(); Thread.sleep(1000); assertTrue(loop.isRunning()); assertFalse(loop.hasLoopFailed()); loop.execute(failedTask); - Thread.sleep(1000); + future.get(); assertFalse(loop.isRunning()); assertTrue(loop.hasLoopFailed()); - assertFalse(thread.mFailed); + assertFalse(mFailed); // Re-spinning the message loop is not allowed after exception. - try { - loop.loop(); - fail(); - } catch (IllegalStateException e) { - // Expected. - } - } - - /** - * A Thread class to move assertion to the main thread, so findbug - * won't complain. - */ - private class TestThread extends Thread { - boolean mFailed = false; - - public TestThread() { - } - - @Override - public void run() { - throw new IllegalStateException(); - } + mExecutorService.submit(new Runnable() { + @Override + public void run() { + try { + loop.loop(); + fail(); + } catch (Exception e) { + if (!(e instanceof IllegalStateException)) { + fail(); + } + } + } + }).get(); } }
diff --git a/components/cronet/android/test/quic_test_server.cc b/components/cronet/android/test/quic_test_server.cc index ff0b859e..f4b14f96 100644 --- a/components/cronet/android/test/quic_test_server.cc +++ b/components/cronet/android/test/quic_test_server.cc
@@ -26,8 +26,7 @@ net::tools::QuicSimpleServer* g_quic_server = nullptr; void ServeFilesFromDirectory( - const base::FilePath& directory, - base::android::ScopedJavaGlobalRef<jobject>* callback) { + const base::FilePath& directory) { DCHECK(g_quic_server_thread->task_runner()->BelongsToCurrentThread()); DCHECK(!g_quic_server); base::FilePath file_dir = directory.Append("quic_data"); @@ -41,7 +40,7 @@ int rv = g_quic_server->Listen(net::IPEndPoint(ip, kServerPort)); CHECK_GE(rv, 0) << "Quic server fails to start"; JNIEnv* env = base::android::AttachCurrentThread(); - Java_QuicTestServer_onServerStarted(env, callback->obj()); + Java_QuicTestServer_onServerStarted(env); } void ShutdownOnServerThread() { @@ -55,7 +54,7 @@ // Quic server is currently hardcoded to run on port 6121 of the localhost on // the device. void StartQuicTestServer(JNIEnv* env, - jclass jcaller, + jclass /*jcaller*/, jstring jtest_files_root) { DCHECK(!g_quic_server_thread); g_quic_server_thread = new base::Thread("quic server thread"); @@ -65,26 +64,22 @@ DCHECK(started); base::FilePath test_files_root( base::android::ConvertJavaStringToUTF8(env, jtest_files_root)); - base::android::ScopedJavaGlobalRef<jobject>* callback = - new base::android::ScopedJavaGlobalRef<jobject>(); - callback->Reset(env, jcaller); g_quic_server_thread->task_runner()->PostTask( - FROM_HERE, base::Bind(&ServeFilesFromDirectory, test_files_root, - base::Owned(callback))); + FROM_HERE, base::Bind(&ServeFilesFromDirectory, test_files_root)); } -void ShutdownQuicTestServer(JNIEnv* env, jclass jcaller) { +void ShutdownQuicTestServer(JNIEnv* env, jclass /*jcaller*/) { DCHECK(!g_quic_server_thread->task_runner()->BelongsToCurrentThread()); g_quic_server_thread->task_runner()->PostTask( FROM_HERE, base::Bind(&ShutdownOnServerThread)); delete g_quic_server_thread; } -jstring GetServerHost(JNIEnv* env, jclass jcaller) { +jstring GetServerHost(JNIEnv* env, jclass /*jcaller*/) { return base::android::ConvertUTF8ToJavaString(env, kServerHost).Release(); } -int GetServerPort(JNIEnv* env, jclass jcaller) { +int GetServerPort(JNIEnv* env, jclass /*jcaller*/) { return kServerPort; }
diff --git a/components/cronet/android/test/src/org/chromium/net/QuicTestServer.java b/components/cronet/android/test/src/org/chromium/net/QuicTestServer.java index 78fbfde7..8d3102c5 100644 --- a/components/cronet/android/test/src/org/chromium/net/QuicTestServer.java +++ b/components/cronet/android/test/src/org/chromium/net/QuicTestServer.java
@@ -40,7 +40,7 @@ } @CalledByNative - private void onServerStarted() { + private static void onServerStarted() { sBlock.open(); }
diff --git a/components/cronet/android/url_request_context_adapter.cc b/components/cronet/android/url_request_context_adapter.cc index 45ed5fb8..8fc3e02 100644 --- a/components/cronet/android/url_request_context_adapter.cc +++ b/components/cronet/android/url_request_context_adapter.cc
@@ -199,8 +199,9 @@ if (VLOG_IS_ON(2)) { net_log_observer_.reset(new NetLogObserver()); - context_->net_log()->DeprecatedAddObserver(net_log_observer_.get(), - net::NetLog::LOG_ALL_BUT_BYTES); + context_->net_log()->DeprecatedAddObserver( + net_log_observer_.get(), + net::NetLogCaptureMode::IncludeCookiesAndCredentials()); } is_context_initialized_ = true;
diff --git a/components/data_reduction_proxy.gypi b/components/data_reduction_proxy.gypi index 8f5c4a0..1a8bf6d 100644 --- a/components/data_reduction_proxy.gypi +++ b/components/data_reduction_proxy.gypi
@@ -147,6 +147,9 @@ 'data_reduction_proxy/core/common/data_reduction_proxy_client_config_parser.cc', 'data_reduction_proxy/core/common/data_reduction_proxy_client_config_parser.h', 'data_reduction_proxy/core/common/data_reduction_proxy_config_values.h', + 'data_reduction_proxy/core/common/data_reduction_proxy_event_creator.cc', + 'data_reduction_proxy/core/common/data_reduction_proxy_event_creator.h', + 'data_reduction_proxy/core/common/data_reduction_proxy_event_storage_delegate.h', 'data_reduction_proxy/core/common/data_reduction_proxy_event_store.cc', 'data_reduction_proxy/core/common/data_reduction_proxy_event_store.h', 'data_reduction_proxy/core/common/data_reduction_proxy_headers.cc', @@ -186,6 +189,8 @@ 'data_reduction_proxy/core/browser/data_reduction_proxy_settings_test_utils.h', 'data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.cc', 'data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.h', + 'data_reduction_proxy/core/common/data_reduction_proxy_event_storage_delegate_test_utils.cc', + 'data_reduction_proxy/core/common/data_reduction_proxy_event_storage_delegate_test_utils.h', 'data_reduction_proxy/core/common/data_reduction_proxy_headers_test_utils.cc', 'data_reduction_proxy/core/common/data_reduction_proxy_headers_test_utils.h', 'data_reduction_proxy/core/common/data_reduction_proxy_params_test_utils.cc',
diff --git a/components/data_reduction_proxy/content/browser/data_reduction_proxy_debug_resource_throttle.cc b/components/data_reduction_proxy/content/browser/data_reduction_proxy_debug_resource_throttle.cc index cc340ca1e..1b00b19 100644 --- a/components/data_reduction_proxy/content/browser/data_reduction_proxy_debug_resource_throttle.cc +++ b/components/data_reduction_proxy/content/browser/data_reduction_proxy_debug_resource_throttle.cc
@@ -45,7 +45,7 @@ void DataReductionProxyDebugResourceThrottle::StartDisplayingBlockingPage( scoped_refptr<DataReductionProxyDebugUIManager> ui_manager, const DataReductionProxyDebugUIManager::BypassResource& resource) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); ui_manager->DisplayBlockingPage(resource); }
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_protocol.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_protocol.cc index cd9c1c3..cdb3e30 100644 --- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_protocol.cc +++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_protocol.cc
@@ -8,7 +8,7 @@ #include "base/time/time.h" #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_stats.h" #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_config.h" -#include "components/data_reduction_proxy/core/common/data_reduction_proxy_event_store.h" +#include "components/data_reduction_proxy/core/common/data_reduction_proxy_event_creator.h" #include "components/data_reduction_proxy/core/common/data_reduction_proxy_headers.h" #include "components/data_reduction_proxy/core/common/data_reduction_proxy_params.h" #include "net/base/load_flags.h" @@ -70,10 +70,10 @@ DataReductionProxyBypassProtocol::DataReductionProxyBypassProtocol( DataReductionProxyConfig* config, - DataReductionProxyEventStore* event_store) - : config_(config), event_store_(event_store) { + DataReductionProxyEventCreator* event_creator) + : config_(config), event_creator_(event_creator) { DCHECK(config_); - DCHECK(event_store_); + DCHECK(event_creator_); net::NetworkChangeNotifier::AddIPAddressObserver(this); } @@ -128,10 +128,9 @@ // command was sent via the data reduction proxy headers bool event_logged = false; DataReductionProxyInfo data_reduction_proxy_info; - DataReductionProxyBypassType bypass_type = - GetDataReductionProxyBypassType( - response_headers, request->url(), request->net_log(), - &data_reduction_proxy_info, event_store_, &event_logged); + DataReductionProxyBypassType bypass_type = GetDataReductionProxyBypassType( + response_headers, request->url(), request->net_log(), + &data_reduction_proxy_info, event_creator_, &event_logged); if (bypass_type == BYPASS_EVENT_TYPE_MISSING_VIA_HEADER_OTHER) { if (DataReductionProxyParams:: @@ -155,7 +154,7 @@ return false; if (!event_logged) { - event_store_->AddBypassTypeEvent( + event_creator_->AddBypassTypeEvent( request->net_log(), bypass_type, request->url(), data_reduction_proxy_info.bypass_duration); }
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_protocol.h b/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_protocol.h index 44ffbc7..49111c8 100644 --- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_protocol.h +++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_protocol.h
@@ -18,7 +18,7 @@ namespace data_reduction_proxy { class DataReductionProxyConfig; -class DataReductionProxyEventStore; +class DataReductionProxyEventCreator; // Class responsible for determining when a response should or should not cause // the data reduction proxy to be bypassed, and to what degree. Owned by the @@ -27,9 +27,10 @@ : public net::NetworkChangeNotifier::IPAddressObserver { public: // Constructs a DataReductionProxyBypassProtocol object. |config| and - // |event_store| must be non-NULL and outlive |this|. - DataReductionProxyBypassProtocol(DataReductionProxyConfig* config, - DataReductionProxyEventStore* event_store); + // |event_creator| must be non-NULL and outlive |this|. + DataReductionProxyBypassProtocol( + DataReductionProxyConfig* config, + DataReductionProxyEventCreator* event_creator); ~DataReductionProxyBypassProtocol() override; @@ -54,7 +55,7 @@ DataReductionProxyConfig* config_; // Must outlive |this|. - DataReductionProxyEventStore* event_store_; + DataReductionProxyEventCreator* event_creator_; // The set of data reduction proxies through which a response has come back // with the data reduction proxy via header since the last network change.
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_protocol_unittest.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_protocol_unittest.cc index c638bdd8..0934c3e6 100644 --- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_protocol_unittest.cc +++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_protocol_unittest.cc
@@ -116,7 +116,7 @@ DataReductionProxyInterceptor* interceptor = new DataReductionProxyInterceptor(test_context_->config(), bypass_stats_.get(), - test_context_->event_store()); + test_context_->event_creator()); scoped_ptr<net::URLRequestJobFactoryImpl> job_factory_impl( new net::URLRequestJobFactoryImpl()); job_factory_.reset(new net::URLRequestInterceptingJobFactory(
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.cc index 6e9f2a5..9b9ba22 100644 --- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.cc +++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.cc
@@ -6,16 +6,17 @@ #include <string> +#include "base/bind.h" +#include "base/bind_helpers.h" #include "base/metrics/histogram.h" #include "base/metrics/sparse_histogram.h" #include "base/single_thread_task_runner.h" #include "base/strings/string_util.h" #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_configurator.h" #include "components/data_reduction_proxy/core/common/data_reduction_proxy_config_values.h" -#include "components/data_reduction_proxy/core/common/data_reduction_proxy_event_store.h" +#include "components/data_reduction_proxy/core/common/data_reduction_proxy_event_creator.h" #include "components/data_reduction_proxy/core/common/data_reduction_proxy_params.h" #include "net/base/load_flags.h" -#include "net/http/http_network_layer.h" #include "net/proxy/proxy_server.h" #include "net/url_request/url_fetcher.h" #include "net/url_request/url_fetcher_delegate.h" @@ -53,42 +54,21 @@ // Checks if the secure proxy is allowed by the carrier by sending a probe. class SecureProxyChecker : public net::URLFetcherDelegate { public: - SecureProxyChecker(net::URLRequestContext* url_request_context, - scoped_refptr<base::SingleThreadTaskRunner> io_task_runner) - : io_task_runner_(io_task_runner) { - DCHECK(io_task_runner_->BelongsToCurrentThread()); - - url_request_context_.reset(new net::URLRequestContext()); - url_request_context_->CopyFrom(url_request_context); - - net::HttpNetworkSession::Params params_modified = - *(url_request_context_->GetNetworkSessionParams()); - params_modified.enable_quic = false; - params_modified.next_protos = net::NextProtosWithSpdyAndQuic(false, false); - - http_network_layer_.reset(new net::HttpNetworkLayer( - new net::HttpNetworkSession(params_modified))); - url_request_context_->set_http_transaction_factory( - http_network_layer_.get()); - - url_request_context_getter_ = new net::TrivialURLRequestContextGetter( - url_request_context_.get(), io_task_runner_); - } + SecureProxyChecker(net::URLRequestContextGetter* url_request_context_getter) + : url_request_context_getter_(url_request_context_getter) {} void OnURLFetchComplete(const net::URLFetcher* source) override { - DCHECK(io_task_runner_->BelongsToCurrentThread()); DCHECK_EQ(source, fetcher_.get()); net::URLRequestStatus status = source->GetStatus(); std::string response; source->GetResponseAsString(&response); - fetcher_callback_.Run(response, status); + fetcher_callback_.Run(response, status, source->GetResponseCode()); } void CheckIfSecureProxyIsAllowed(const GURL& secure_proxy_check_url, FetcherResponseCallback fetcher_callback) { - DCHECK(io_task_runner_->BelongsToCurrentThread()); fetcher_.reset(net::URLFetcher::Create(secure_proxy_check_url, net::URLFetcher::GET, this)); fetcher_->SetLoadFlags(net::LOAD_DISABLE_CACHE | net::LOAD_BYPASS_PROXY); @@ -111,11 +91,7 @@ ~SecureProxyChecker() override {} private: - scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_; - scoped_refptr<net::URLRequestContextGetter> url_request_context_getter_; - scoped_ptr<net::URLRequestContext> url_request_context_; - scoped_ptr<net::HttpNetworkLayer> http_network_layer_; // The URLFetcher being used for the secure proxy check. scoped_ptr<net::URLFetcher> fetcher_; @@ -129,7 +105,7 @@ net::NetLog* net_log, scoped_ptr<DataReductionProxyConfigValues> config_values, DataReductionProxyConfigurator* configurator, - DataReductionProxyEventStore* event_store) + DataReductionProxyEventCreator* event_creator) : restricted_by_carrier_(false), disabled_on_vpn_(false), unreachable_(false), @@ -139,11 +115,11 @@ io_task_runner_(io_task_runner), net_log_(net_log), configurator_(configurator), - event_store_(event_store), + event_creator_(event_creator), url_request_context_getter_(nullptr) { DCHECK(io_task_runner); DCHECK(configurator); - DCHECK(event_store); + DCHECK(event_creator); } DataReductionProxyConfig::~DataReductionProxyConfig() { @@ -396,11 +372,14 @@ } void DataReductionProxyConfig::HandleSecureProxyCheckResponse( - const std::string& response, const net::URLRequestStatus& status) { + const std::string& response, + const net::URLRequestStatus& status, + int http_response_code) { DCHECK(io_task_runner_->BelongsToCurrentThread()); - if (event_store_) { - event_store_->EndSecureProxyCheck(bound_net_log_, status.error()); - } + bool success_response = ("OK" == response.substr(0, 2)); + if (event_creator_) + event_creator_->EndSecureProxyCheck(bound_net_log_, status.error(), + http_response_code, success_response); if (status.status() == net::URLRequestStatus::FAILED) { if (status.error() == net::ERR_INTERNET_DISCONNECTED) { @@ -414,7 +393,7 @@ std::abs(status.error())); } - if ("OK" == response.substr(0, 2)) { + if (success_response) { DVLOG(1) << "The data reduction proxy is unrestricted."; if (enabled_by_user_) { @@ -508,16 +487,14 @@ DCHECK(io_task_runner_->BelongsToCurrentThread()); bound_net_log_ = net::BoundNetLog::Make( net_log_, net::NetLog::SOURCE_DATA_REDUCTION_PROXY); - - if (event_store_) { - event_store_->BeginSecureProxyCheck( + if (event_creator_) { + event_creator_->BeginSecureProxyCheck( bound_net_log_, config_values_->secure_proxy_check_url()); } if (!secure_proxy_checker_) { - DCHECK(url_request_context_getter_->GetURLRequestContext()); - secure_proxy_checker_.reset(new SecureProxyChecker( - url_request_context_getter_->GetURLRequestContext(), io_task_runner_)); + secure_proxy_checker_.reset( + new SecureProxyChecker(url_request_context_getter_)); } secure_proxy_checker_->CheckIfSecureProxyIsAllowed(secure_proxy_check_url, fetcher_callback);
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.h b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.h index 9acf691a..4499ee42 100644 --- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.h +++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.h
@@ -38,12 +38,13 @@ namespace data_reduction_proxy { -typedef base::Callback<void(const std::string&, const net::URLRequestStatus&)> - FetcherResponseCallback; +typedef base::Callback<void(const std::string&, + const net::URLRequestStatus&, + int)> FetcherResponseCallback; class DataReductionProxyConfigValues; class DataReductionProxyConfigurator; -class DataReductionProxyEventStore; +class DataReductionProxyEventCreator; class DataReductionProxyService; class SecureProxyChecker; struct DataReductionProxyTypeInfo; @@ -81,14 +82,19 @@ : public net::NetworkChangeNotifier::IPAddressObserver { public: // The caller must ensure that all parameters remain alive for the lifetime - // of the |DataReductionProxyConfig| instance, with the exception of |params| - // which this instance will own. + // of the |DataReductionProxyConfig| instance, with the exception of + // |config_values| which is owned by |this|. |io_task_runner| is used to + // validate calls on the correct thread. |event_creator| is used for logging + // the start and end of a secure proxy check; |net_log| is used to create a + // net::BoundNetLog for correlating the start and end of the check. + // |config_values| contains the Data Reduction Proxy configuration values. + // |configurator| is the target for a configuration update. DataReductionProxyConfig( scoped_refptr<base::SingleThreadTaskRunner> io_task_runner, net::NetLog* net_log, scoped_ptr<DataReductionProxyConfigValues> config_values, DataReductionProxyConfigurator* configurator, - DataReductionProxyEventStore* event_store); + DataReductionProxyEventCreator* event_creator); ~DataReductionProxyConfig() override; // Performs initialization on the IO thread. @@ -229,8 +235,9 @@ // Parses the secure proxy check responses and appropriately configures the // Data Reduction Proxy rules. - virtual void HandleSecureProxyCheckResponse( - const std::string& response, const net::URLRequestStatus& status); + void HandleSecureProxyCheckResponse(const std::string& response, + const net::URLRequestStatus& status, + int http_response_code); // Adds the default proxy bypass rules for the Data Reduction Proxy. void AddDefaultProxyBypassRules(); @@ -278,8 +285,8 @@ // The caller must ensure that the |configurator_| outlives this instance. DataReductionProxyConfigurator* configurator_; - // The caller must ensure that the |event_store_| outlives this instance. - DataReductionProxyEventStore* event_store_; + // The caller must ensure that the |event_creator_| outlives this instance. + DataReductionProxyEventCreator* event_creator_; // Used for performing the secure proxy check. net::URLRequestContextGetter* url_request_context_getter_;
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 af4c571..67728a9 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
@@ -9,6 +9,7 @@ #include "base/bind.h" #include "base/bind_helpers.h" +#include "base/command_line.h" #include "base/json/json_writer.h" #include "base/location.h" #include "base/logging.h" @@ -19,14 +20,22 @@ #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.h" #include "components/data_reduction_proxy/core/common/data_reduction_proxy_client_config_parser.h" #include "components/data_reduction_proxy/core/common/data_reduction_proxy_params.h" +#include "components/data_reduction_proxy/core/common/data_reduction_proxy_switches.h" #include "components/data_reduction_proxy/proto/client_config.pb.h" #include "net/base/host_port_pair.h" +#include "net/base/load_flags.h" +#include "net/http/http_status_code.h" #include "net/proxy/proxy_server.h" +#include "net/url_request/url_fetcher.h" +#include "net/url_request/url_request_status.h" namespace data_reduction_proxy { namespace { +// Default URL for retrieving the Data Reduction Proxy configuration. +const char kClientConfigURL[] = ""; + // This is the default backoff policy used to communicate with the Data // Reduction Proxy configuration service. const net::BackoffEntry::Policy kDefaultBackoffPolicy = { @@ -80,6 +89,23 @@ return kDefaultBackoffPolicy; } +// static +GURL DataReductionProxyConfigServiceClient::GetConfigServiceURL( + const base::CommandLine& command_line) { + if (!command_line.HasSwitch(switches::kDataReductionProxyConfigURL)) + return GURL(kClientConfigURL); + + std::string value( + command_line.GetSwitchValueASCII(switches::kDataReductionProxyConfigURL)); + GURL result(value); + if (result.is_valid()) + return result; + + LOG(WARNING) << "The following client config URL specified at the " + << "command-line is invalid: " << value; + return GURL(kClientConfigURL); +} + DataReductionProxyConfigServiceClient::DataReductionProxyConfigServiceClient( scoped_ptr<DataReductionProxyParams> params, const net::BackoffEntry::Policy& backoff_policy, @@ -90,7 +116,11 @@ request_options_(request_options), config_values_(config_values), config_(config), - backoff_entry_(&backoff_policy) { + backoff_entry_(&backoff_policy), + config_service_url_( + GetConfigServiceURL(*base::CommandLine::ForCurrentProcess())), + use_local_config_(!config_service_url_.is_valid()), + url_request_context_getter_(nullptr) { DCHECK(request_options); DCHECK(config_values); DCHECK(config); @@ -100,47 +130,24 @@ DataReductionProxyConfigServiceClient:: ~DataReductionProxyConfigServiceClient() { + net::NetworkChangeNotifier::RemoveIPAddressObserver(this); +} + +void DataReductionProxyConfigServiceClient::InitializeOnIOThread( + net::URLRequestContextGetter* url_request_context_getter) { + DCHECK(url_request_context_getter); + net::NetworkChangeNotifier::AddIPAddressObserver(this); + url_request_context_getter_ = url_request_context_getter; } void DataReductionProxyConfigServiceClient::RetrieveConfig() { DCHECK(thread_checker_.CalledOnValidThread()); - std::string static_response = ConstructStaticResponse(); - ClientConfig config; - bool succeeded = false; - if (config_parser::ParseClientConfig(static_response, &config)) { - if (config.has_proxy_config()) { - net::ProxyServer origin; - net::ProxyServer fallback_origin; - std::vector<net::ProxyServer> proxies = - GetProxiesForHTTP(config.proxy_config()); - if (proxies.size() > 0) { - origin = proxies[0]; - if (proxies.size() > 1) - fallback_origin = proxies[1]; - - std::string session; - std::string credentials; - if (DataReductionProxyRequestOptions::ParseLocalSessionKey( - config.session_key(), &session, &credentials)) { - request_options_->SetCredentials(session, credentials); - config_values_->UpdateValues(origin, fallback_origin); - config_->ReloadConfig(); - succeeded = true; - } - } - } + if (use_local_config_) { + ReadAndApplyStaticConfig(); + return; } - base::Time expiration_time; - if (succeeded) { - expiration_time = config_parser::TimestampToTime(config.expire_time()); - } - - GetBackoffEntry()->InformOfRequest(succeeded); - base::TimeDelta next_config_refresh_time = - CalculateNextConfigRefreshTime(succeeded, expiration_time, Now(), - GetBackoffEntry()->GetTimeUntilRelease()); - SetConfigRefreshTimer(next_config_refresh_time); + RetrieveRemoteConfig(); } net::BackoffEntry* DataReductionProxyConfigServiceClient::GetBackoffEntry() { @@ -171,4 +178,114 @@ return response; } +void DataReductionProxyConfigServiceClient::OnIPAddressChanged() { + GetBackoffEntry()->Reset(); + RetrieveConfig(); +} + +void DataReductionProxyConfigServiceClient::OnURLFetchComplete( + const net::URLFetcher* source) { + DCHECK(source == fetcher_.get()); + net::URLRequestStatus status = source->GetStatus(); + std::string response; + source->GetResponseAsString(&response); + HandleResponse(response, status, source->GetResponseCode()); +} + +void DataReductionProxyConfigServiceClient::ReadAndApplyStaticConfig() { + std::string static_response = ConstructStaticResponse(); + HandleResponse(static_response, net::URLRequestStatus(), net::HTTP_OK); +} + +void DataReductionProxyConfigServiceClient::RetrieveRemoteConfig() { + scoped_ptr<net::URLFetcher> fetcher = + GetURLFetcherForConfig(config_service_url_, std::string()); + if (!fetcher.get()) { + HandleResponse(std::string(), + net::URLRequestStatus(net::URLRequestStatus::CANCELED, 0), + net::URLFetcher::RESPONSE_CODE_INVALID); + return; + } + + fetcher_ = fetcher.Pass(); + fetcher_->Start(); +} + +scoped_ptr<net::URLFetcher> +DataReductionProxyConfigServiceClient::GetURLFetcherForConfig( + const GURL& secure_proxy_check_url, + const std::string& request_body) { + scoped_ptr<net::URLFetcher> fetcher(net::URLFetcher::Create( + secure_proxy_check_url, net::URLFetcher::POST, this)); + fetcher->SetLoadFlags(net::LOAD_BYPASS_PROXY); + fetcher->SetUploadData("application/json", request_body); + DCHECK(url_request_context_getter_); + fetcher->SetRequestContext(url_request_context_getter_); + // Configure max retries to be at most kMaxRetries times for 5xx errors. + static const int kMaxRetries = 5; + fetcher->SetMaxRetriesOn5xx(kMaxRetries); + fetcher->SetAutomaticallyRetryOnNetworkChanges(kMaxRetries); + return fetcher.Pass(); +} + +void DataReductionProxyConfigServiceClient::HandleResponse( + const std::string& config_data, + const net::URLRequestStatus& status, + int response_code) { + ClientConfig config; + bool succeeded = false; + + if (status.status() == net::URLRequestStatus::SUCCESS && + response_code == net::HTTP_OK && + config_parser::ParseClientConfig(config_data, &config)) { + succeeded = ParseAndApplyProxyConfig(config); + } + + base::Time expiration_time; + if (succeeded) { + expiration_time = config_parser::TimestampToTime(config.expire_time()); + } + + GetBackoffEntry()->InformOfRequest(succeeded); + base::TimeDelta next_config_refresh_time = + CalculateNextConfigRefreshTime(succeeded, expiration_time, Now(), + GetBackoffEntry()->GetTimeUntilRelease()); + SetConfigRefreshTimer(next_config_refresh_time); +} + +bool DataReductionProxyConfigServiceClient::ParseAndApplyProxyConfig( + const ClientConfig& config) { + if (!config.has_proxy_config()) + return false; + + std::vector<net::ProxyServer> proxies = + GetProxiesForHTTP(config.proxy_config()); + if (proxies.empty()) + return false; + + net::ProxyServer origin = proxies[0]; + net::ProxyServer fallback_origin; + if (proxies.size() > 1) + fallback_origin = proxies[1]; + + if (!use_local_config_) { + request_options_->SetSecureSession(config.session_key()); + config_values_->UpdateValues(origin, fallback_origin); + config_->ReloadConfig(); + return true; + } + + std::string session; + std::string credentials; + if (!DataReductionProxyRequestOptions::ParseLocalSessionKey( + config.session_key(), &session, &credentials)) { + return false; + } + + request_options_->SetCredentials(session, credentials); + config_values_->UpdateValues(origin, fallback_origin); + config_->ReloadConfig(); + return true; +} + } // 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 84878295..41b0424 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
@@ -13,18 +13,30 @@ #include "base/threading/thread_checker.h" #include "base/timer/timer.h" #include "net/base/backoff_entry.h" +#include "net/base/network_change_notifier.h" +#include "net/url_request/url_fetcher_delegate.h" +#include "url/gurl.h" namespace base { +class CommandLine; class Time; class TimeDelta; } +namespace net { +class URLFetcher; +class URLRequestContextGetter; +class URLRequestStatus; +} + namespace data_reduction_proxy { +class ClientConfig; class DataReductionProxyConfig; class DataReductionProxyMutableConfigValues; class DataReductionProxyParams; class DataReductionProxyRequestOptions; +class DataReductionProxyService; // Retrieves the default net::BackoffEntry::Policy for the Data Reduction Proxy // configuration service client. @@ -32,11 +44,19 @@ // Retrieves the Data Reduction Proxy configuration from a remote service. This // object lives on the IO thread. -class DataReductionProxyConfigServiceClient { +// TODO(jeremyim): Rename the class to DataReductionProxyConfigGetter(?). +class DataReductionProxyConfigServiceClient + : public net::NetworkChangeNotifier::IPAddressObserver, + public net::URLFetcherDelegate { public: + // Returns the URL from which the Data Reduction Proxy configuration should + // be retrieved. + static GURL GetConfigServiceURL(const base::CommandLine& command_line); + // The caller must ensure that all parameters remain alive for the lifetime of // the |DataReductionProxyConfigClient|, with the exception of |params| - // which this instance will own. + // which this instance will own. The |io_task_runner| is used to enforce that + // configurations are applied on the IO thread. DataReductionProxyConfigServiceClient( scoped_ptr<DataReductionProxyParams> params, const net::BackoffEntry::Policy& backoff_policy, @@ -44,9 +64,14 @@ DataReductionProxyMutableConfigValues* config_values, DataReductionProxyConfig* config); - virtual ~DataReductionProxyConfigServiceClient(); + ~DataReductionProxyConfigServiceClient() override; - // Request the retrieval of the Data Reduction Proxy configuration. + // Performs initialization on the IO thread. + void InitializeOnIOThread( + net::URLRequestContextGetter* url_request_context_getter); + + // Request the retrieval of the Data Reduction Proxy configuration. This + // operation takes place asynchronously. void RetrieveConfig(); protected: @@ -54,24 +79,55 @@ // Virtual for testing. virtual net::BackoffEntry* GetBackoffEntry(); - // Sets a timer to determine when to next refresh the Data Reduction Proxy - // configuration. - // Virtual for testing. - virtual void SetConfigRefreshTimer(const base::TimeDelta& delay); - // Returns the current time. // Virtual for testing. virtual base::Time Now(); + // Sets a timer to determine when to next refresh the Data Reduction Proxy + // configuration. + void SetConfigRefreshTimer(const base::TimeDelta& delay); + // Constructs a synthetic response based on |params_|. - // Virtual for testing. - virtual std::string ConstructStaticResponse() const; + std::string ConstructStaticResponse() const; private: FRIEND_TEST_ALL_PREFIXES(DataReductionProxyConfigServiceClientTest, TestConstructStaticResponse); + FRIEND_TEST_ALL_PREFIXES(DataReductionProxyConfigServiceClientTest, + OnIPAddressChange); friend class TestDataReductionProxyConfigServiceClient; + // Override of net::NetworkChangeNotifier::IPAddressObserver. + void OnIPAddressChanged() override; + + // Override of net::URLFetcherDelegate. + void OnURLFetchComplete(const net::URLFetcher* source) override; + + // Retrieves the Data Reduction Proxy configuration from |params_|. + void ReadAndApplyStaticConfig(); + + // Retrieves the Data Reduction Proxy configuration from a remote service. + void RetrieveRemoteConfig(); + + // Returns a fetcher to retrieve the Data Reduction Proxy configuration. + // |secure_proxy_check_url| is the url from which to retrieve the config. + // |request_body| is the request body sent to the configuration service. + scoped_ptr<net::URLFetcher> GetURLFetcherForConfig( + const GURL& secure_proxy_check_url, + const std::string& request_body); + + // Handles the response from the remote Data Reduction Proxy configuration + // service. |response| is the response body, |status| is the + // |net::URLRequestStatus| of the response, and response_code is the HTTP + // response code (if available). + void HandleResponse(const std::string& response, + const net::URLRequestStatus& status, + int response_code); + + // Parses out the proxy configuration portion of |config| and applies it to + // |config_| and |request_options_|. + bool ParseAndApplyProxyConfig(const ClientConfig& config); + // Contains the static configuration data to use. scoped_ptr<DataReductionProxyParams> params_; @@ -87,11 +143,25 @@ // Used to calculate the backoff time on request failures. net::BackoffEntry backoff_entry_; + // The URL for retrieving the Data Reduction Proxy configuration. + GURL config_service_url_; + + // Whether to use |params_| to obtain the Data Reduction Proxy configuration + // or the remote server specified by |config_service_url_|. + // TODO(jeremyim): Remove this as part of bug 479282. + bool use_local_config_; + + // Used for setting up |fetcher_|. + net::URLRequestContextGetter* url_request_context_getter_; + // An event that fires when it is time to refresh the Data Reduction Proxy // configuration. base::OneShotTimer<DataReductionProxyConfigServiceClient> config_refresh_timer_; + // A |net::URLFetcher| to retrieve the Data Reduction Proxy configuration. + scoped_ptr<net::URLFetcher> fetcher_; + // Enforce usage on the IO thread. base::ThreadChecker thread_checker_;
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 b72ee5746..19ec5f4 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
@@ -6,6 +6,7 @@ #include <string> +#include "base/command_line.h" #include "base/memory/scoped_ptr.h" #include "base/time/tick_clock.h" #include "base/values.h" @@ -15,9 +16,28 @@ #include "components/data_reduction_proxy/core/common/data_reduction_proxy_client_config_parser.h" #include "components/data_reduction_proxy/core/common/data_reduction_proxy_params.h" #include "components/data_reduction_proxy/core/common/data_reduction_proxy_params_test_utils.h" +#include "components/data_reduction_proxy/core/common/data_reduction_proxy_switches.h" #include "components/data_reduction_proxy/proto/client_config.pb.h" +#include "net/socket/socket_test_util.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" +#include "url/gurl.h" + +namespace { + +const char kSuccessResponse[] = + "{ \"sessionKey\": \"SecretSessionKey\", " + "\"expireTime\": \"1970-01-01T00:01:00.000Z\", " + "\"proxyConfig\": { \"httpProxyServers\": [" + "{ \"scheme\": \"HTTPS\", \"host\": \"origin.net\", \"port\": 443 }," + "{ \"scheme\": \"HTTP\", \"host\": \"fallback.net\", \"port\": 80 }" + "] } }"; +// The following values should match the ones in the response above. +const char kSuccessOrigin[] = "https://origin.net:443"; +const char kSuccessFallback[] = "fallback.net:80"; +const char kSuccessSessionKey[] = "SecretSessionKey"; + +} // namespace namespace data_reduction_proxy { @@ -50,6 +70,8 @@ class DataReductionProxyConfigServiceClientTest : public testing::Test { protected: + DataReductionProxyConfigServiceClientTest() : context_(true) {} + void SetUp() override { test_context_ = DataReductionProxyTestContext::Builder() @@ -57,12 +79,15 @@ DataReductionProxyParams::kFallbackAllowed | DataReductionProxyParams::kPromoAllowed) .WithParamsDefinitions(TestDataReductionProxyParams::HAS_EVERYTHING) + .WithURLRequestContext(&context_) + .WithMockClientSocketFactory(&mock_socket_factory_) .WithTestConfigurator() .WithMockRequestOptions() .WithTestConfigClient() .Build(); - test_context_->test_config_client()->SetCustomReleaseTime( - base::TimeTicks::UnixEpoch()); + context_.set_client_socket_factory(&mock_socket_factory_); + context_.Init(); + ResetBackoffEntryReleaseTime(); test_context_->test_config_client()->SetNow(base::Time::UnixEpoch()); } @@ -84,6 +109,18 @@ test_context_->io_data()->config())); } + void ResetBackoffEntryReleaseTime() { + config_client()->SetCustomReleaseTime(base::TimeTicks::UnixEpoch()); + } + + void VerifyRemoteSuccess() { + EXPECT_EQ(base::TimeDelta::FromMinutes(1), config_client()->GetDelay()); + EXPECT_EQ(kSuccessOrigin, configurator()->origin()); + EXPECT_EQ(kSuccessFallback, configurator()->fallback_origin()); + EXPECT_TRUE(configurator()->ssl_origin().empty()); + EXPECT_EQ(kSuccessSessionKey, request_options()->GetSecureSession()); + } + DataReductionProxyParams* params() { return test_context_->test_params(); } @@ -104,7 +141,14 @@ test_context_->RunUntilIdle(); } + net::MockClientSocketFactory* mock_socket_factory() { + return &mock_socket_factory_; + } + private: + net::TestURLRequestContext context_; + net::MockClientSocketFactory mock_socket_factory_; + scoped_ptr<DataReductionProxyTestContext> test_context_; scoped_ptr<DataReductionProxyRequestOptions> request_options_; }; @@ -118,6 +162,8 @@ } TEST_F(DataReductionProxyConfigServiceClientTest, SuccessfulLoop) { + // Use a local/static config. + config_client()->SetConfigServiceURL(GURL()); RequestOptionsPopulator populator( base::Time::UnixEpoch() + base::TimeDelta::FromDays(1), base::TimeDelta::FromDays(1)); @@ -148,6 +194,8 @@ } TEST_F(DataReductionProxyConfigServiceClientTest, SuccessfulLoopShortDuration) { + // Use a local/static config. + config_client()->SetConfigServiceURL(GURL()); RequestOptionsPopulator populator( base::Time::UnixEpoch() + base::TimeDelta::FromSeconds(1), base::TimeDelta::FromSeconds(1)); @@ -168,6 +216,8 @@ } TEST_F(DataReductionProxyConfigServiceClientTest, EnsureBackoff) { + // Use a local/static config. + config_client()->SetConfigServiceURL(GURL()); SetDataReductionProxyEnabled(true); EXPECT_TRUE(configurator()->origin().empty()); EXPECT_TRUE(configurator()->fallback_origin().empty()); @@ -188,6 +238,8 @@ } TEST_F(DataReductionProxyConfigServiceClientTest, ConfigDisabled) { + // Use a local/static config. + config_client()->SetConfigServiceURL(GURL()); RequestOptionsPopulator populator( base::Time::UnixEpoch() + base::TimeDelta::FromDays(1), base::TimeDelta::FromDays(1)); @@ -206,4 +258,133 @@ EXPECT_EQ(base::TimeDelta::FromDays(1), config_client()->GetDelay()); } +TEST_F(DataReductionProxyConfigServiceClientTest, GetConfigServiceURL) { + const struct { + std::string flag_value; + GURL expected; + } tests[] = { + { + "", GURL(), + }, + { + "http://configservice.chrome-test.com", + GURL("http://configservice.chrome-test.com"), + }, + }; + + for (const auto& test : tests) { + // Reset all flags. + base::CommandLine::ForCurrentProcess()->InitFromArgv(0, NULL); + base::CommandLine::ForCurrentProcess()->AppendSwitchASCII( + switches::kDataReductionProxyConfigURL, test.flag_value); + EXPECT_EQ(test.expected, + DataReductionProxyConfigServiceClient::GetConfigServiceURL( + *base::CommandLine::ForCurrentProcess())); + } +} + +TEST_F(DataReductionProxyConfigServiceClientTest, RemoteConfigSuccess) { + net::MockRead mock_reads[] = { + net::MockRead("HTTP/1.1 200 OK\r\n\r\n"), + net::MockRead(kSuccessResponse), + net::MockRead(net::SYNCHRONOUS, net::OK), + }; + net::StaticSocketDataProvider socket_data_provider( + mock_reads, arraysize(mock_reads), nullptr, 0); + mock_socket_factory()->AddSocketDataProvider(&socket_data_provider); + config_client()->SetConfigServiceURL(GURL("http://configservice.com")); + SetDataReductionProxyEnabled(true); + EXPECT_TRUE(configurator()->origin().empty()); + EXPECT_TRUE(configurator()->fallback_origin().empty()); + EXPECT_TRUE(configurator()->ssl_origin().empty()); + EXPECT_CALL(*request_options(), PopulateConfigResponse(testing::_)).Times(0); + config_client()->RetrieveConfig(); + RunUntilIdle(); + VerifyRemoteSuccess(); +} + +TEST_F(DataReductionProxyConfigServiceClientTest, + RemoteConfigSuccessAfterFailure) { + net::MockRead mock_reads_array[][3] = { + { + // Failure due to 404 error. + net::MockRead("HTTP/1.1 404 Not found\r\n\r\n"), + net::MockRead(""), + net::MockRead(net::SYNCHRONOUS, net::OK), + }, + { + // Success. + net::MockRead("HTTP/1.1 200 OK\r\n\r\n"), + net::MockRead(kSuccessResponse), + net::MockRead(net::SYNCHRONOUS, net::OK), + }, + }; + + ScopedVector<net::SocketDataProvider> socket_data_providers; + for (net::MockRead* mock_reads : mock_reads_array) { + socket_data_providers.push_back( + new net::StaticSocketDataProvider(mock_reads, 3, nullptr, 0)); + mock_socket_factory()->AddSocketDataProvider(socket_data_providers.back()); + } + + config_client()->SetConfigServiceURL(GURL("http://configservice.com")); + SetDataReductionProxyEnabled(true); + EXPECT_TRUE(configurator()->origin().empty()); + EXPECT_TRUE(configurator()->fallback_origin().empty()); + EXPECT_TRUE(configurator()->ssl_origin().empty()); + EXPECT_CALL(*request_options(), PopulateConfigResponse(testing::_)).Times(0); + config_client()->RetrieveConfig(); + RunUntilIdle(); + EXPECT_EQ(base::TimeDelta::FromSeconds(20), config_client()->GetDelay()); + EXPECT_TRUE(configurator()->origin().empty()); + EXPECT_TRUE(configurator()->fallback_origin().empty()); + EXPECT_TRUE(configurator()->ssl_origin().empty()); + EXPECT_TRUE(request_options()->GetSecureSession().empty()); + config_client()->RetrieveConfig(); + RunUntilIdle(); + VerifyRemoteSuccess(); +} + +TEST_F(DataReductionProxyConfigServiceClientTest, OnIPAddressChange) { + config_client()->SetConfigServiceURL(GURL("http://configservice.com")); + SetDataReductionProxyEnabled(true); + config_client()->RetrieveConfig(); + EXPECT_CALL(*request_options(), PopulateConfigResponse(testing::_)).Times(0); + + static const int kFailureCount = 5; + + net::MockRead failure_reads[] = { + net::MockRead("HTTP/1.1 404 Not found\r\n\r\n"), + net::MockRead(""), + net::MockRead(net::SYNCHRONOUS, net::OK), + }; + + ScopedVector<net::SocketDataProvider> socket_data_providers; + for (int i = 0; i < kFailureCount; ++i) { + socket_data_providers.push_back( + new net::StaticSocketDataProvider(failure_reads, 3, nullptr, 0)); + mock_socket_factory()->AddSocketDataProvider(socket_data_providers.back()); + config_client()->RetrieveConfig(); + RunUntilIdle(); + } + + EXPECT_EQ(base::TimeDelta::FromSeconds(320), config_client()->GetDelay()); + EXPECT_EQ(kFailureCount, config_client()->GetBackoffErrorCount()); + config_client()->OnIPAddressChanged(); + EXPECT_EQ(0, config_client()->GetBackoffErrorCount()); + ResetBackoffEntryReleaseTime(); + + net::MockRead success_reads[] = { + net::MockRead("HTTP/1.1 200 OK\r\n\r\n"), + net::MockRead(kSuccessResponse), + net::MockRead(net::SYNCHRONOUS, net::OK), + }; + net::StaticSocketDataProvider socket_data_provider( + success_reads, arraysize(success_reads), nullptr, 0); + mock_socket_factory()->AddSocketDataProvider(&socket_data_provider); + config_client()->RetrieveConfig(); + RunUntilIdle(); + VerifyRemoteSuccess(); +} + } // namespace data_reduction_proxy
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_test_utils.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_test_utils.cc index dbbde520..43b5140 100644 --- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_test_utils.cc +++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_test_utils.cc
@@ -21,15 +21,15 @@ scoped_refptr<base::SingleThreadTaskRunner> task_runner, net::NetLog* net_log, DataReductionProxyConfigurator* configurator, - DataReductionProxyEventStore* event_store) + DataReductionProxyEventCreator* event_creator) : TestDataReductionProxyConfig( - make_scoped_ptr( - new TestDataReductionProxyParams(params_flags, - params_definitions)).Pass(), + make_scoped_ptr(new TestDataReductionProxyParams(params_flags, + params_definitions)) + .Pass(), task_runner, net_log, configurator, - event_store) { + event_creator) { } TestDataReductionProxyConfig::TestDataReductionProxyConfig( @@ -37,12 +37,12 @@ scoped_refptr<base::SingleThreadTaskRunner> task_runner, net::NetLog* net_log, DataReductionProxyConfigurator* configurator, - DataReductionProxyEventStore* event_store) + DataReductionProxyEventCreator* event_creator) : DataReductionProxyConfig(task_runner, net_log, config_values.Pass(), configurator, - event_store) { + event_creator) { network_interfaces_.reset(new net::NetworkInterfaceList()); } @@ -92,12 +92,12 @@ scoped_refptr<base::SingleThreadTaskRunner> task_runner, net::NetLog* net_log, DataReductionProxyConfigurator* configurator, - DataReductionProxyEventStore* event_store) + DataReductionProxyEventCreator* event_creator) : TestDataReductionProxyConfig(config_values.Pass(), task_runner, net_log, configurator, - event_store) { + event_creator) { } MockDataReductionProxyConfig::~MockDataReductionProxyConfig() { @@ -112,9 +112,4 @@ restricted, at_startup); } -void MockDataReductionProxyConfig::HandleSecureProxyCheckResponse( - const std::string& response, const net::URLRequestStatus& status) { - DataReductionProxyConfig::HandleSecureProxyCheckResponse(response, status); -} - } // namespace data_reduction_proxy
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_test_utils.h b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_test_utils.h index 759b29ce..a07dc59a 100644 --- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_test_utils.h +++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_test_utils.h
@@ -22,7 +22,7 @@ namespace data_reduction_proxy { class DataReductionProxyConfigurator; -class DataReductionProxyEventStore; +class DataReductionProxyEventCreator; class DataReductionProxyMutableConfigValues; class TestDataReductionProxyParams; @@ -39,7 +39,7 @@ scoped_refptr<base::SingleThreadTaskRunner> network_task_runner, net::NetLog* net_log, DataReductionProxyConfigurator* configurator, - DataReductionProxyEventStore* event_store); + DataReductionProxyEventCreator* event_creator); // Creates a |TestDataReductionProxyConfig| with the provided |config_values|. // This permits any DataReductionProxyConfigValues to be used (such as @@ -49,7 +49,7 @@ scoped_refptr<base::SingleThreadTaskRunner> task_runner, net::NetLog* net_log, DataReductionProxyConfigurator* configurator, - DataReductionProxyEventStore* event_store); + DataReductionProxyEventCreator* event_creator); ~TestDataReductionProxyConfig() override; @@ -92,7 +92,7 @@ scoped_refptr<base::SingleThreadTaskRunner> network_task_runner, net::NetLog* net_log, DataReductionProxyConfigurator* configurator, - DataReductionProxyEventStore* event_store); + DataReductionProxyEventCreator* event_creator); ~MockDataReductionProxyConfig(); MOCK_METHOD1(RecordSecureProxyCheckFetchResult, @@ -125,12 +125,6 @@ bool alternative_enabled, bool restricted, bool at_startup) override; - - // HandleSecureProxyCheckResponse should always call - // RecordSecureProxyCheckFetchResult exactly once. - void HandleSecureProxyCheckResponse( - const std::string& response, - const net::URLRequestStatus& status) override; }; } // namespace data_reduction_proxy
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_unittest.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_unittest.cc index 1b9c9d9..6dfb7b8 100644 --- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_unittest.cc +++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_unittest.cc
@@ -10,9 +10,10 @@ #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_configurator_test_utils.h" #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_service.h" #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.h" -#include "components/data_reduction_proxy/core/common/data_reduction_proxy_event_store.h" +#include "components/data_reduction_proxy/core/common/data_reduction_proxy_event_creator.h" #include "components/data_reduction_proxy/core/common/data_reduction_proxy_params_test_utils.h" #include "components/data_reduction_proxy/core/common/data_reduction_proxy_switches.h" +#include "net/http/http_status_code.h" #include "net/log/test_net_log.h" #include "net/proxy/proxy_server.h" #include "net/url_request/test_url_fetcher_factory.h" @@ -109,11 +110,12 @@ class TestResponder { public: void ExecuteCallback(FetcherResponseCallback callback) { - callback.Run(response, status); + callback.Run(response, status, http_response_code); } std::string response; net::URLRequestStatus status; + int http_response_code; }; void CheckSecureProxyCheckOnIPChange( @@ -131,6 +133,7 @@ responder.response = response; responder.status = net::URLRequestStatus(net::URLRequestStatus::SUCCESS, net::OK); + responder.http_response_code = net::HTTP_OK; EXPECT_CALL(*config(), SecureProxyCheck(_, _)) .Times(1) .WillRepeatedly(testing::WithArgs<1>( @@ -160,7 +163,7 @@ params->EnableQuic(false); return make_scoped_ptr(new DataReductionProxyConfig( test_context_->task_runner(), test_context_->net_log(), params.Pass(), - test_context_->configurator(), test_context_->event_store())); + test_context_->configurator(), test_context_->event_creator())); } MockDataReductionProxyConfig* config() {
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_configurator.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_configurator.cc index 2a804f1..35c8828 100644 --- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_configurator.cc +++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_configurator.cc
@@ -6,17 +6,17 @@ #include "base/strings/string_util.h" #include "base/values.h" -#include "components/data_reduction_proxy/core/common/data_reduction_proxy_event_store.h" +#include "components/data_reduction_proxy/core/common/data_reduction_proxy_event_creator.h" #include "net/proxy/proxy_config.h" namespace data_reduction_proxy { DataReductionProxyConfigurator::DataReductionProxyConfigurator( net::NetLog* net_log, - DataReductionProxyEventStore* event_store) - : net_log_(net_log), data_reduction_proxy_event_store_(event_store) { + DataReductionProxyEventCreator* event_creator) + : net_log_(net_log), data_reduction_proxy_event_creator_(event_creator) { DCHECK(net_log); - DCHECK(event_store); + DCHECK(event_creator); // Constructed on the UI thread, but should be checked on the IO thread. thread_checker_.DetachFromThread(); } @@ -63,7 +63,7 @@ // config will return invalid. net::ProxyConfig::ID unused_id = 1; config.set_id(unused_id); - data_reduction_proxy_event_store_->AddProxyEnabledEvent( + data_reduction_proxy_event_creator_->AddProxyEnabledEvent( net_log_, primary_restricted, fallback_restricted, primary_origin, fallback_origin, ssl_origin); config_ = config; @@ -72,7 +72,7 @@ void DataReductionProxyConfigurator::Disable() { DCHECK(thread_checker_.CalledOnValidThread()); net::ProxyConfig config = net::ProxyConfig::CreateDirect(); - data_reduction_proxy_event_store_->AddProxyDisabledEvent(net_log_); + data_reduction_proxy_event_creator_->AddProxyDisabledEvent(net_log_); config_ = config; }
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_configurator.h b/components/data_reduction_proxy/core/browser/data_reduction_proxy_configurator.h index e239b542..f62bb7f 100644 --- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_configurator.h +++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_configurator.h
@@ -22,16 +22,15 @@ namespace data_reduction_proxy { -class DataReductionProxyEventStore; +class DataReductionProxyEventCreator; class DataReductionProxyConfigurator { public: - // Constructs a configurator. |net_log| and |event_store| are used to + // Constructs a configurator. |net_log| and |event_creator| are used to // track network and Data Reduction Proxy events respectively, must not be // null, and must outlive this instance. - DataReductionProxyConfigurator( - net::NetLog* net_log, - DataReductionProxyEventStore* event_store); + DataReductionProxyConfigurator(net::NetLog* net_log, + DataReductionProxyEventCreator* event_creator); virtual ~DataReductionProxyConfigurator(); @@ -76,7 +75,7 @@ // Used for logging of network- and Data Reduction Proxy-related events. net::NetLog* net_log_; - DataReductionProxyEventStore* data_reduction_proxy_event_store_; + DataReductionProxyEventCreator* data_reduction_proxy_event_creator_; // Enforce usage on the IO thread. base::ThreadChecker thread_checker_;
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_configurator_test_utils.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_configurator_test_utils.cc index e373aad..27d9d38 100644 --- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_configurator_test_utils.cc +++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_configurator_test_utils.cc
@@ -8,8 +8,8 @@ TestDataReductionProxyConfigurator::TestDataReductionProxyConfigurator( net::NetLog* net_log, - DataReductionProxyEventStore* event_store) - : DataReductionProxyConfigurator(net_log, event_store), + DataReductionProxyEventCreator* event_creator) + : DataReductionProxyConfigurator(net_log, event_creator), enabled_(false), restricted_(false), fallback_restricted_(false) {
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_configurator_test_utils.h b/components/data_reduction_proxy/core/browser/data_reduction_proxy_configurator_test_utils.h index bcfccd5..9125bfd 100644 --- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_configurator_test_utils.h +++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_configurator_test_utils.h
@@ -15,14 +15,14 @@ namespace data_reduction_proxy { -class DataReductionProxyEventStore; +class DataReductionProxyEventCreator; class TestDataReductionProxyConfigurator : public DataReductionProxyConfigurator { public: TestDataReductionProxyConfigurator( net::NetLog* net_log, - DataReductionProxyEventStore* event_store); + DataReductionProxyEventCreator* event_creator); ~TestDataReductionProxyConfigurator() override; // Overrides of DataReductionProxyConfigurator
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_configurator_unittest.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_configurator_unittest.cc index ee10e1d..d669dcd 100644 --- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_configurator_unittest.cc +++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_configurator_unittest.cc
@@ -7,10 +7,10 @@ #include <string> #include "base/memory/scoped_ptr.h" -#include "base/test/test_simple_task_runner.h" #include "base/values.h" -#include "components/data_reduction_proxy/core/common/data_reduction_proxy_event_store.h" -#include "net/log/test_net_log.h" +#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.h" +#include "components/data_reduction_proxy/core/common/data_reduction_proxy_params.h" +#include "components/data_reduction_proxy/core/common/data_reduction_proxy_params_test_utils.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -19,12 +19,18 @@ class DataReductionProxyConfiguratorTest : public testing::Test { public: void SetUp() override { - task_runner_ = new base::TestSimpleTaskRunner(); - net_log_.reset(new net::TestNetLog()); - data_reduction_proxy_event_store_.reset( - new data_reduction_proxy::DataReductionProxyEventStore(task_runner_)); + test_context_ = + DataReductionProxyTestContext::Builder() + .WithParamsFlags(DataReductionProxyParams::kAllowed | + DataReductionProxyParams::kFallbackAllowed | + DataReductionProxyParams::kPromoAllowed) + .WithParamsDefinitions( + TestDataReductionProxyParams::HAS_EVERYTHING & + ~TestDataReductionProxyParams::HAS_DEV_ORIGIN & + ~TestDataReductionProxyParams::HAS_DEV_FALLBACK_ORIGIN) + .Build(); config_.reset(new DataReductionProxyConfigurator( - net_log_.get(), data_reduction_proxy_event_store_.get())); + test_context_->net_log(), test_context_->event_creator())); } void CheckProxyConfig( @@ -32,7 +38,7 @@ const std::string& expected_http_proxies, const std::string& expected_https_proxies, const std::string& expected_bypass_list) { - task_runner_->RunUntilIdle(); + test_context_->RunUntilIdle(); const net::ProxyConfig::ProxyRules& rules = config_->GetProxyConfig().proxy_rules(); ASSERT_EQ(expected_rules_type, rules.type); @@ -44,11 +50,8 @@ } } + scoped_ptr<DataReductionProxyTestContext> test_context_; scoped_ptr<DataReductionProxyConfigurator> config_; - scoped_ptr<net::NetLog> net_log_; - scoped_refptr<base::TestSimpleTaskRunner> task_runner_; - scoped_ptr<data_reduction_proxy::DataReductionProxyEventStore> - data_reduction_proxy_event_store_; }; TEST_F(DataReductionProxyConfiguratorTest, TestUnrestricted) {
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_interceptor.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_interceptor.cc index 28de78c..8a1145a6 100644 --- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_interceptor.cc +++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_interceptor.cc
@@ -6,6 +6,7 @@ #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_protocol.h" #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_stats.h" +#include "components/data_reduction_proxy/core/common/data_reduction_proxy_event_creator.h" #include "components/data_reduction_proxy/core/common/data_reduction_proxy_headers.h" #include "net/http/http_response_headers.h" #include "net/url_request/url_request.h" @@ -19,10 +20,10 @@ DataReductionProxyInterceptor::DataReductionProxyInterceptor( DataReductionProxyConfig* config, DataReductionProxyBypassStats* stats, - DataReductionProxyEventStore* event_store) + DataReductionProxyEventCreator* event_creator) : bypass_stats_(stats), bypass_protocol_( - new DataReductionProxyBypassProtocol(config, event_store)) { + new DataReductionProxyBypassProtocol(config, event_creator)) { } DataReductionProxyInterceptor::~DataReductionProxyInterceptor() {
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_interceptor.h b/components/data_reduction_proxy/core/browser/data_reduction_proxy_interceptor.h index ab7ece8..33c360a121 100644 --- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_interceptor.h +++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_interceptor.h
@@ -11,7 +11,7 @@ namespace data_reduction_proxy { class DataReductionProxyBypassProtocol; class DataReductionProxyConfig; -class DataReductionProxyEventStore; +class DataReductionProxyEventCreator; class DataReductionProxyBypassStats; // Used to intercept responses that contain explicit and implicit signals @@ -20,11 +20,11 @@ // without use of the proxy. class DataReductionProxyInterceptor : public net::URLRequestInterceptor { public: - // Constructs the interceptor. |config|, |stats|, and |event_store| must + // Constructs the interceptor. |config|, |stats|, and |event_creator| must // outlive |this|. |stats| may be NULL. DataReductionProxyInterceptor(DataReductionProxyConfig* config, DataReductionProxyBypassStats* stats, - DataReductionProxyEventStore* event_store); + DataReductionProxyEventCreator* event_creator); // Destroys the interceptor. ~DataReductionProxyInterceptor() override;
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.cc index 9034bae2..5a4c5d8 100644 --- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.cc +++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.cc
@@ -6,6 +6,7 @@ #include "base/bind.h" #include "base/command_line.h" +#include "base/macros.h" #include "base/memory/weak_ptr.h" #include "base/prefs/pref_member.h" #include "base/single_thread_task_runner.h" @@ -21,26 +22,87 @@ #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_network_delegate.h" #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_service.h" #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.h" -#include "components/data_reduction_proxy/core/common/data_reduction_proxy_event_store.h" +#include "components/data_reduction_proxy/core/common/data_reduction_proxy_event_creator.h" +#include "components/data_reduction_proxy/core/common/data_reduction_proxy_event_storage_delegate.h" #include "components/data_reduction_proxy/core/common/data_reduction_proxy_params.h" #include "components/data_reduction_proxy/core/common/data_reduction_proxy_switches.h" #include "net/log/net_log.h" +#include "net/url_request/http_user_agent_settings.h" +#include "net/url_request/static_http_user_agent_settings.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 data_reduction_proxy { +// A |net::URLRequestContextGetter| which uses only vanilla HTTP/HTTPS for +// performing requests. This is used by the secure proxy check to prevent the +// use of SPDY and QUIC which may be used by the primary request contexts. +class BasicHTTPURLRequestContextGetter : public net::URLRequestContextGetter { + public: + BasicHTTPURLRequestContextGetter( + const std::string& user_agent, + const scoped_refptr<base::SingleThreadTaskRunner>& network_task_runner); + + // Overridden from net::URLRequestContextGetter: + net::URLRequestContext* GetURLRequestContext() override; + scoped_refptr<base::SingleThreadTaskRunner> GetNetworkTaskRunner() + const override; + + private: + ~BasicHTTPURLRequestContextGetter() override; + + scoped_refptr<base::SingleThreadTaskRunner> network_task_runner_; + scoped_ptr<net::HttpUserAgentSettings> user_agent_settings_; + scoped_ptr<net::URLRequestContext> url_request_context_; + + DISALLOW_COPY_AND_ASSIGN(BasicHTTPURLRequestContextGetter); +}; + +BasicHTTPURLRequestContextGetter::BasicHTTPURLRequestContextGetter( + const std::string& user_agent, + const scoped_refptr<base::SingleThreadTaskRunner>& network_task_runner) + : network_task_runner_(network_task_runner), + user_agent_settings_( + new net::StaticHttpUserAgentSettings(std::string(), user_agent)) { +} + +net::URLRequestContext* +BasicHTTPURLRequestContextGetter::GetURLRequestContext() { + if (!url_request_context_) { + net::URLRequestContextBuilder builder; + builder.set_proxy_service(net::ProxyService::CreateDirect()); + builder.SetSpdyAndQuicEnabled(false, false); + url_request_context_.reset(builder.Build()); + } + + return url_request_context_.get(); +} + +scoped_refptr<base::SingleThreadTaskRunner> +BasicHTTPURLRequestContextGetter::GetNetworkTaskRunner() const { + return network_task_runner_; +} + +BasicHTTPURLRequestContextGetter::~BasicHTTPURLRequestContextGetter() { +} + DataReductionProxyIOData::DataReductionProxyIOData( const Client& client, int param_flags, net::NetLog* net_log, scoped_refptr<base::SingleThreadTaskRunner> io_task_runner, scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner, - bool enable_quic) + bool enable_quic, + const std::string& user_agent) : client_(client), net_log_(net_log), io_task_runner_(io_task_runner), ui_task_runner_(ui_task_runner), shutdown_on_ui_(false), url_request_context_getter_(nullptr), + basic_url_request_context_getter_( + new BasicHTTPURLRequestContextGetter(user_agent, io_task_runner)), weak_factory_(this) { DCHECK(net_log); DCHECK(io_task_runner_); @@ -48,9 +110,9 @@ scoped_ptr<DataReductionProxyParams> params( new DataReductionProxyParams(param_flags)); params->EnableQuic(enable_quic); - event_store_.reset(new DataReductionProxyEventStore(ui_task_runner)); + event_creator_.reset(new DataReductionProxyEventCreator(this)); configurator_.reset( - new DataReductionProxyConfigurator(net_log, event_store_.get())); + new DataReductionProxyConfigurator(net_log, event_creator_.get())); bool use_config_client = DataReductionProxyParams::IsConfigClientEnabled(); DataReductionProxyMutableConfigValues* raw_mutable_config = nullptr; if (use_config_client) { @@ -59,11 +121,11 @@ raw_mutable_config = mutable_config.get(); config_.reset(new DataReductionProxyConfig( io_task_runner_, net_log, mutable_config.Pass(), configurator_.get(), - event_store_.get())); + event_creator_.get())); } else { - config_.reset( - new DataReductionProxyConfig(io_task_runner_, net_log, params.Pass(), - configurator_.get(), event_store_.get())); + config_.reset(new DataReductionProxyConfig( + io_task_runner_, net_log, params.Pass(), configurator_.get(), + event_creator_.get())); } // It is safe to use base::Unretained here, since it gets executed @@ -124,9 +186,9 @@ void DataReductionProxyIOData::InitializeOnIOThread() { DCHECK(io_task_runner_->BelongsToCurrentThread()); - config_->InitializeOnIOThread(url_request_context_getter_); + config_->InitializeOnIOThread(basic_url_request_context_getter_.get()); if (config_client_.get()) - config_client_->RetrieveConfig(); + config_client_->InitializeOnIOThread(url_request_context_getter_); ui_task_runner_->PostTask( FROM_HERE, base::Bind(&DataReductionProxyService::SetIOData, @@ -140,11 +202,17 @@ switches::kEnableDataReductionProxy); } +void DataReductionProxyIOData::RetrieveConfig() { + DCHECK(io_task_runner_->BelongsToCurrentThread()); + if (config_client_) + config_client_->RetrieveConfig(); +} + scoped_ptr<net::URLRequestInterceptor> DataReductionProxyIOData::CreateInterceptor() { DCHECK(io_task_runner_->BelongsToCurrentThread()); return make_scoped_ptr(new DataReductionProxyInterceptor( - config_.get(), bypass_stats_.get(), event_store_.get())); + config_.get(), bypass_stats_.get(), event_creator_.get())); } scoped_ptr<DataReductionProxyNetworkDelegate> @@ -181,6 +249,34 @@ data_reduction_proxy_enabled, request_type)); } +void DataReductionProxyIOData::AddEnabledEvent(scoped_ptr<base::Value> entry, + bool enabled) { + DCHECK(io_task_runner_->BelongsToCurrentThread()); + ui_task_runner_->PostTask( + FROM_HERE, base::Bind(&DataReductionProxyService::AddEnabledEvent, + service_, base::Passed(&entry), enabled)); +} + +void DataReductionProxyIOData::AddEventAndSecureProxyCheckState( + scoped_ptr<base::Value> entry, + SecureProxyCheckState state) { + DCHECK(io_task_runner_->BelongsToCurrentThread()); + ui_task_runner_->PostTask( + FROM_HERE, + base::Bind(&DataReductionProxyService::AddEventAndSecureProxyCheckState, + service_, base::Passed(&entry), state)); +} + +void DataReductionProxyIOData::AddAndSetLastBypassEvent( + scoped_ptr<base::Value> entry, + int64 expiration_ticks) { + DCHECK(io_task_runner_->BelongsToCurrentThread()); + ui_task_runner_->PostTask( + FROM_HERE, + base::Bind(&DataReductionProxyService::AddAndSetLastBypassEvent, service_, + base::Passed(&entry), expiration_ticks)); +} + void DataReductionProxyIOData::SetUnreachable(bool unreachable) { DCHECK(io_task_runner_->BelongsToCurrentThread()); ui_task_runner_->PostTask(
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.h b/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.h index 7e45c16..9a6f3b8 100644 --- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.h +++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.h
@@ -5,6 +5,8 @@ #ifndef COMPONENTS_DATA_REDUCTION_PROXY_CORE_BROWSER_DATA_REDUCTION_PROXY_IO_DATA_H_ #define COMPONENTS_DATA_REDUCTION_PROXY_CORE_BROWSER_DATA_REDUCTION_PROXY_IO_DATA_H_ +#include "base/gtest_prod_util.h" +#include "base/macros.h" #include "base/memory/scoped_ptr.h" #include "base/memory/weak_ptr.h" #include "base/prefs/pref_member.h" @@ -13,6 +15,11 @@ #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_metrics.h" #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_network_delegate.h" #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.h" +#include "components/data_reduction_proxy/core/common/data_reduction_proxy_event_storage_delegate.h" + +namespace base { +class Value; +} namespace net { class NetLog; @@ -26,12 +33,12 @@ class DataReductionProxyConfig; class DataReductionProxyConfigServiceClient; class DataReductionProxyConfigurator; -class DataReductionProxyEventStore; +class DataReductionProxyEventCreator; class DataReductionProxyService; // Contains and initializes all Data Reduction Proxy objects that operate on // the IO thread. -class DataReductionProxyIOData { +class DataReductionProxyIOData : public DataReductionProxyEventStorageDelegate { public: // Constructs a DataReductionProxyIOData object. |param_flags| is used to // set information about the DNS names used by the proxy, and allowable @@ -42,7 +49,8 @@ net::NetLog* net_log, scoped_refptr<base::SingleThreadTaskRunner> io_task_runner, scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner, - bool enable_quic); + bool enable_quic, + const std::string& user_agent); virtual ~DataReductionProxyIOData(); @@ -57,6 +65,8 @@ void SetDataReductionProxyService( base::WeakPtr<DataReductionProxyService> data_reduction_proxy_service); + void RetrieveConfig(); + // Creates an interceptor suitable for following the Data Reduction Proxy // bypass protocol. scoped_ptr<net::URLRequestInterceptor> CreateInterceptor(); @@ -83,6 +93,14 @@ bool data_reduction_proxy_enabled, DataReductionProxyRequestType request_type); + // Overrides of DataReductionProxyEventStorageDelegate. Bridges to the UI + // thread objects. + void AddEnabledEvent(scoped_ptr<base::Value> entry, bool enabled) override; + void AddEventAndSecureProxyCheckState(scoped_ptr<base::Value> entry, + SecureProxyCheckState state) override; + void AddAndSetLastBypassEvent(scoped_ptr<base::Value> entry, + int64 expiration_ticks) override; + // Returns true if the Data Reduction Proxy is enabled and false otherwise. bool IsEnabled() const; @@ -95,14 +113,18 @@ return config_.get(); } - DataReductionProxyEventStore* event_store() const { - return event_store_.get(); + DataReductionProxyEventCreator* event_creator() const { + return event_creator_.get(); } DataReductionProxyRequestOptions* request_options() const { return request_options_.get(); } + DataReductionProxyConfigServiceClient* config_client() const { + return config_client_.get(); + } + net::ProxyDelegate* proxy_delegate() const { return proxy_delegate_.get(); } @@ -131,6 +153,7 @@ private: friend class TestDataReductionProxyIOData; + FRIEND_TEST_ALL_PREFIXES(DataReductionProxyIODataTest, TestConstruction); // Used for testing. DataReductionProxyIOData(); @@ -153,8 +176,8 @@ // interstitials. mutable scoped_ptr<DataReductionProxyDebugUIService> debug_ui_service_; - // Tracker of Data Reduction Proxy-related events, e.g., for logging. - scoped_ptr<DataReductionProxyEventStore> event_store_; + // Creates Data Reduction Proxy-related events for logging. + scoped_ptr<DataReductionProxyEventCreator> event_creator_; // Setter of the Data Reduction Proxy-specific proxy configuration. scoped_ptr<DataReductionProxyConfigurator> configurator_; @@ -192,6 +215,10 @@ // The net::URLRequestContextGetter used for making URL requests. net::URLRequestContextGetter* url_request_context_getter_; + // A net::URLRequestContextGetter used for making secure proxy checks. It + // does not use alternate protocols. + scoped_refptr<net::URLRequestContextGetter> basic_url_request_context_getter_; + base::WeakPtrFactory<DataReductionProxyIOData> weak_factory_; DISALLOW_COPY_AND_ASSIGN(DataReductionProxyIOData);
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data_unittest.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data_unittest.cc index c9c92e1..4caf215 100644 --- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data_unittest.cc +++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data_unittest.cc
@@ -14,7 +14,11 @@ #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_prefs.h" #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.h" #include "components/data_reduction_proxy/core/common/data_reduction_proxy_params.h" +#include "net/http/http_network_session.h" #include "net/log/net_log.h" +#include "net/socket/next_proto.h" +#include "net/url_request/url_request_context.h" +#include "net/url_request/url_request_context_getter.h" #include "net/url_request/url_request_interceptor.h" #include "net/url_request/url_request_test_util.h" #include "testing/gtest/include/gtest/gtest.h" @@ -90,7 +94,22 @@ TEST_F(DataReductionProxyIODataTest, TestConstruction) { scoped_ptr<DataReductionProxyIOData> io_data(new DataReductionProxyIOData( Client::UNKNOWN, DataReductionProxyParams::kAllowed, net_log(), - message_loop_proxy(), message_loop_proxy(), false /* enable_quic */)); + message_loop_proxy(), message_loop_proxy(), false /* enable_quic */, + std::string() /* user_agent */)); + + // Check that the SimpleURLRequestContextGetter uses vanilla HTTP. + net::URLRequestContext* request_context = + io_data->basic_url_request_context_getter_.get()->GetURLRequestContext(); + const net::HttpNetworkSession::Params* http_params = + request_context->GetNetworkSessionParams(); + EXPECT_TRUE(http_params->use_alternate_protocols); + EXPECT_FALSE(http_params->enable_quic); + net::NextProtoVector expected_protos = + net::NextProtosWithSpdyAndQuic(false, false); + EXPECT_EQ(expected_protos.size(), http_params->next_protos.size()); + size_t proto_index = 0; + for (const auto& proto : expected_protos) + EXPECT_EQ(proto, http_params->next_protos[proto_index++]); // Check that io_data creates an interceptor. Such an interceptor is // thoroughly tested by DataReductionProxyInterceptoTest.
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.cc index 8bd3d23..12b67856 100644 --- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.cc +++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.cc
@@ -4,6 +4,8 @@ #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.h" +#include <vector> + #include "base/bind.h" #include "base/command_line.h" #include "base/single_thread_task_runner.h" @@ -40,6 +42,7 @@ const char kSessionHeaderOption[] = "ps"; const char kCredentialsHeaderOption[] = "sid"; +const char kSecureSessionHeaderOption[] = "s"; const char kBuildNumberHeaderOption[] = "b"; const char kPatchNumberHeaderOption[] = "p"; const char kClientHeaderOption[] = "c"; @@ -300,6 +303,19 @@ DCHECK(thread_checker_.CalledOnValidThread()); session_ = session; credentials_ = credentials; + secure_session_.clear(); + // Force skipping of credential regeneration. It should be handled by the + // caller. + use_assigned_credentials_ = true; + RegenerateRequestHeaderValue(); +} + +void DataReductionProxyRequestOptions::SetSecureSession( + const std::string& secure_session) { + DCHECK(thread_checker_.CalledOnValidThread()); + session_.clear(); + credentials_.clear(); + secure_session_ = secure_session; // Force skipping of credential regeneration. It should be handled by the // caller. use_assigned_credentials_ = true; @@ -326,6 +342,10 @@ return key; } +const std::string& DataReductionProxyRequestOptions::GetSecureSession() const { + return secure_session_; +} + void DataReductionProxyRequestOptions::MaybeAddRequestHeaderImpl( const net::HostPortPair& proxy_server, bool expect_ssl, @@ -341,18 +361,27 @@ } void DataReductionProxyRequestOptions::RegenerateRequestHeaderValue() { - header_value_ = FormatOption(kSessionHeaderOption, session_) - + ", " + FormatOption(kCredentialsHeaderOption, credentials_) - + (client_.empty() ? - "" : ", " + FormatOption(kClientHeaderOption, client_)) - + (build_.empty() || patch_.empty() ? - "" : - ", " + FormatOption(kBuildNumberHeaderOption, build_) - + ", " + FormatOption(kPatchNumberHeaderOption, patch_)) - + (lofi_.empty() ? - "" : ", " + FormatOption(kLoFiHeaderOption, lofi_)); + std::vector<std::string> headers; + if (!session_.empty()) + headers.push_back(FormatOption(kSessionHeaderOption, session_)); + if (!credentials_.empty()) + headers.push_back(FormatOption(kCredentialsHeaderOption, credentials_)); + if (!secure_session_.empty()) { + headers.push_back( + FormatOption(kSecureSessionHeaderOption, secure_session_)); + } + if (!client_.empty()) + headers.push_back(FormatOption(kClientHeaderOption, client_)); + if (!build_.empty() && !patch_.empty()) { + headers.push_back(FormatOption(kBuildNumberHeaderOption, build_)); + headers.push_back(FormatOption(kPatchNumberHeaderOption, patch_)); + } + if (!lofi_.empty()) + headers.push_back(FormatOption(kLoFiHeaderOption, lofi_)); for (const auto& experiment : experiments_) - header_value_ += ", " + FormatOption(kExperimentsOption, experiment); + headers.push_back(FormatOption(kExperimentsOption, experiment)); + + header_value_ = JoinString(headers, ", "); } } // namespace data_reduction_proxy
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.h b/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.h index 2aa8dce..0721dab5 100644 --- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.h +++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.h
@@ -29,6 +29,7 @@ extern const char kSessionHeaderOption[]; extern const char kCredentialsHeaderOption[]; +extern const char kSecureSessionHeaderOption[]; extern const char kBuildNumberHeaderOption[]; extern const char kPatchNumberHeaderOption[]; extern const char kClientHeaderOption[]; @@ -126,6 +127,9 @@ void SetCredentials(const std::string& session, const std::string& credentials); + // Sets the credentials for sending to the Data Reduction Proxy. + void SetSecureSession(const std::string& secure_session); + protected: void SetHeader(net::HttpRequestHeaders* headers); @@ -146,6 +150,9 @@ const std::string& version, DataReductionProxyConfig* config); + // Visible for testing. + virtual const std::string& GetSecureSession() const; + private: FRIEND_TEST_ALL_PREFIXES(DataReductionProxyRequestOptionsTest, AuthHashForSalt); @@ -201,6 +208,7 @@ std::string version_; std::string session_; std::string credentials_; + std::string secure_session_; std::string build_; std::string patch_; std::string lofi_;
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options_unittest.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options_unittest.cc index 95c3465..4ea8a9a 100644 --- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options_unittest.cc +++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options_unittest.cc
@@ -35,6 +35,8 @@ const char kExpectedCredentials2[] = "c911fdb402f578787562cf7f00eda972"; const char kExpectedSession2[] = "0-1633771873-1633771873-1633771873"; const char kDataReductionProxyKey[] = "12345"; + +const char kSecureSession[] = "TestSecureSessionKey"; } // namespace @@ -78,6 +80,7 @@ void SetHeaderExpectations(const std::string& session, const std::string& credentials, + const std::string& secure_session, const std::string& client, const std::string& build, const std::string& patch, @@ -93,6 +96,10 @@ expected_options.push_back( std::string(kCredentialsHeaderOption) + "=" + credentials); } + if (!secure_session.empty()) { + expected_options.push_back(std::string(kSecureSessionHeaderOption) + "=" + + secure_session); + } if (!client.empty()) { expected_options.push_back( std::string(kClientHeaderOption) + "=" + client); @@ -182,15 +189,17 @@ TEST_F(DataReductionProxyRequestOptionsTest, AuthorizationOnIOThread) { std::string expected_header; - SetHeaderExpectations(kExpectedSession2, kExpectedCredentials2, kClientStr, - kExpectedBuild, kExpectedPatch, std::string(), - std::vector<std::string>(), &expected_header); + SetHeaderExpectations(kExpectedSession2, kExpectedCredentials2, std::string(), + kClientStr, kExpectedBuild, kExpectedPatch, + std::string(), std::vector<std::string>(), + &expected_header); std::string expected_header2; SetHeaderExpectations("86401-1633771873-1633771873-1633771873", - "d7c1c34ef6b90303b01c48a6c1db6419", kClientStr, - kExpectedBuild, kExpectedPatch, std::string(), - std::vector<std::string>(), &expected_header2); + "d7c1c34ef6b90303b01c48a6c1db6419", std::string(), + kClientStr, kExpectedBuild, kExpectedPatch, + std::string(), std::vector<std::string>(), + &expected_header2); CreateRequestOptions(kVersion); test_context_->RunUntilIdle(); @@ -233,9 +242,10 @@ TEST_F(DataReductionProxyRequestOptionsTest, AuthorizationIgnoresEmptyKey) { std::string expected_header; - SetHeaderExpectations(kExpectedSession, kExpectedCredentials, kClientStr, - kExpectedBuild, kExpectedPatch, std::string(), - std::vector<std::string>(), &expected_header); + SetHeaderExpectations(kExpectedSession, kExpectedCredentials, std::string(), + kClientStr, kExpectedBuild, kExpectedPatch, + std::string(), std::vector<std::string>(), + &expected_header); CreateRequestOptions(kVersion); VerifyExpectedHeader(params()->DefaultOrigin(), expected_header); @@ -247,8 +257,8 @@ TEST_F(DataReductionProxyRequestOptionsTest, AuthorizationBogusVersion) { std::string expected_header; - SetHeaderExpectations(kExpectedSession2, kExpectedCredentials2, kClientStr, - std::string(), std::string(), std::string(), + SetHeaderExpectations(kExpectedSession2, kExpectedCredentials2, std::string(), + kClientStr, std::string(), std::string(), std::string(), std::vector<std::string>(), &expected_header); CreateRequestOptions(kBogusVersion); @@ -260,8 +270,8 @@ TEST_F(DataReductionProxyRequestOptionsTest, AuthorizationLoFi) { std::string expected_header; - SetHeaderExpectations(kExpectedSession, kExpectedCredentials, kClientStr, - std::string(), std::string(), "low", + SetHeaderExpectations(kExpectedSession, kExpectedCredentials, std::string(), + kClientStr, std::string(), std::string(), "low", std::vector<std::string>(), &expected_header); base::CommandLine::ForCurrentProcess()->AppendSwitch( @@ -277,14 +287,25 @@ data_reduction_proxy::switches::kEnableDataReductionProxyLoFi); std::string expected_header; - SetHeaderExpectations(kExpectedSession, kExpectedCredentials, kClientStr, - std::string(), std::string(), "low", + SetHeaderExpectations(kExpectedSession, kExpectedCredentials, std::string(), + kClientStr, std::string(), std::string(), "low", std::vector<std::string>(), &expected_header); CreateRequestOptions(kBogusVersion); VerifyExpectedHeader(params()->DefaultOrigin(), expected_header); } +TEST_F(DataReductionProxyRequestOptionsTest, SecureSession) { + std::string expected_header; + SetHeaderExpectations(std::string(), std::string(), kSecureSession, + kClientStr, std::string(), std::string(), std::string(), + std::vector<std::string>(), &expected_header); + + CreateRequestOptions(kBogusVersion); + request_options()->SetSecureSession(kSecureSession); + VerifyExpectedHeader(params()->DefaultOrigin(), expected_header); +} + TEST_F(DataReductionProxyRequestOptionsTest, ParseExperiments) { base::CommandLine::ForCurrentProcess()->AppendSwitchASCII( data_reduction_proxy::switches::kDataReductionProxyExperiment, @@ -293,8 +314,8 @@ expected_experiments.push_back("staging"); expected_experiments.push_back("\"foo,bar\""); std::string expected_header; - SetHeaderExpectations(kExpectedSession, kExpectedCredentials, kClientStr, - std::string(), std::string(), std::string(), + SetHeaderExpectations(kExpectedSession, kExpectedCredentials, std::string(), + kClientStr, std::string(), std::string(), std::string(), expected_experiments, &expected_header); CreateRequestOptions(kBogusVersion);
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_service.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_service.cc index 1b90a14..cf25df9 100644 --- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_service.cc +++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_service.cc
@@ -11,6 +11,7 @@ #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.h" #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_service_observer.h" #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.h" +#include "components/data_reduction_proxy/core/common/data_reduction_proxy_event_store.h" namespace data_reduction_proxy { @@ -26,6 +27,7 @@ weak_factory_(this) { DCHECK(settings); compression_stats_ = compression_stats.Pass(); + event_store_.reset(new DataReductionProxyEventStore()); } DataReductionProxyService::~DataReductionProxyService() { @@ -68,6 +70,26 @@ } } +void DataReductionProxyService::AddEnabledEvent(scoped_ptr<base::Value> entry, + bool enabled) { + DCHECK(CalledOnValidThread()); + event_store_->AddEnabledEvent(entry.Pass(), enabled); +} + +void DataReductionProxyService::AddEventAndSecureProxyCheckState( + scoped_ptr<base::Value> entry, + SecureProxyCheckState state) { + DCHECK(CalledOnValidThread()); + event_store_->AddEventAndSecureProxyCheckState(entry.Pass(), state); +} + +void DataReductionProxyService::AddAndSetLastBypassEvent( + scoped_ptr<base::Value> entry, + int64 expiration_ticks) { + DCHECK(CalledOnValidThread()); + event_store_->AddAndSetLastBypassEvent(entry.Pass(), expiration_ticks); +} + void DataReductionProxyService::SetUnreachable(bool unreachable) { DCHECK(CalledOnValidThread()); settings_->SetUnreachable(unreachable); @@ -83,6 +105,13 @@ io_data_, enabled, alternative_enabled, at_startup)); } +void DataReductionProxyService::RetrieveConfig() { + DCHECK(CalledOnValidThread()); + io_task_runner_->PostTask( + FROM_HERE, + base::Bind(&DataReductionProxyIOData::RetrieveConfig, io_data_)); +} + void DataReductionProxyService::AddObserver( DataReductionProxyServiceObserver* observer) { DCHECK(CalledOnValidThread());
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_service.h b/components/data_reduction_proxy/core/browser/data_reduction_proxy_service.h index ed393073..54a3365 100644 --- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_service.h +++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_service.h
@@ -14,6 +14,7 @@ #include "base/single_thread_task_runner.h" #include "base/threading/non_thread_safe.h" #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_metrics.h" +#include "components/data_reduction_proxy/core/common/data_reduction_proxy_event_storage_delegate.h" class GURL; class PrefService; @@ -21,6 +22,7 @@ namespace base { class SequencedTaskRunner; class TimeDelta; +class Value; } namespace net { @@ -30,13 +32,16 @@ namespace data_reduction_proxy { class DataReductionProxyCompressionStats; +class DataReductionProxyEventStore; class DataReductionProxyIOData; class DataReductionProxyServiceObserver; class DataReductionProxySettings; // Contains and initializes all Data Reduction Proxy objects that have a // lifetime based on the UI thread. -class DataReductionProxyService : public base::NonThreadSafe { +class DataReductionProxyService + : public base::NonThreadSafe, + public DataReductionProxyEventStorageDelegate { public: // The caller must ensure that |settings| and |request_context| remain alive // for the lifetime of the |DataReductionProxyService| instance. This instance @@ -73,6 +78,13 @@ bool data_reduction_proxy_enabled, DataReductionProxyRequestType request_type); + // Overrides of DataReductionProxyEventStorageDelegate. + void AddEnabledEvent(scoped_ptr<base::Value> entry, bool enabled) override; + void AddEventAndSecureProxyCheckState(scoped_ptr<base::Value> entry, + SecureProxyCheckState state) override; + void AddAndSetLastBypassEvent(scoped_ptr<base::Value> entry, + int64 expiration_ticks) override; + // Records whether the Data Reduction Proxy is unreachable or not. void SetUnreachable(bool unreachable); @@ -81,6 +93,7 @@ virtual void SetProxyPrefs(bool enabled, bool alternative_enabled, bool at_startup); + void RetrieveConfig(); // Methods for adding/removing observers on |this|. void AddObserver(DataReductionProxyServiceObserver* observer); @@ -95,6 +108,10 @@ return settings_; } + DataReductionProxyEventStore* event_store() const { + return event_store_.get(); + } + net::URLRequestContextGetter* url_request_context_getter() const { return url_request_context_getter_; } @@ -107,6 +124,8 @@ // Tracks compression statistics to be displayed to the user. scoped_ptr<DataReductionProxyCompressionStats> compression_stats_; + scoped_ptr<DataReductionProxyEventStore> event_store_; + DataReductionProxySettings* settings_; // Used to post tasks to |io_data_|.
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.cc index 408ad45c..57f18bb6 100644 --- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.cc +++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.cc
@@ -49,7 +49,6 @@ alternative_allowed_(false), promo_allowed_(false), prefs_(NULL), - event_store_(NULL), config_(nullptr) { } @@ -88,11 +87,9 @@ DCHECK(prefs); DCHECK(io_data); DCHECK(io_data->config()); - DCHECK(io_data->event_store()); DCHECK(data_reduction_proxy_service.get()); prefs_ = prefs; config_ = io_data->config(); - event_store_ = io_data->event_store(); data_reduction_proxy_service_ = data_reduction_proxy_service.Pass(); data_reduction_proxy_service_->AddObserver(this); InitPrefMembers(); @@ -226,6 +223,7 @@ data_reduction_proxy_service_->SetProxyPrefs( IsDataReductionProxyEnabled(), IsDataReductionProxyAlternativeEnabled(), at_startup); + data_reduction_proxy_service_->RetrieveConfig(); } void DataReductionProxySettings::MaybeActivateDataReductionProxy( @@ -251,6 +249,14 @@ UpdateIOData(at_startup); } +DataReductionProxyEventStore* DataReductionProxySettings::GetEventStore() + const { + if (data_reduction_proxy_service_) + return data_reduction_proxy_service_->event_store(); + + return nullptr; +} + // Metrics methods void DataReductionProxySettings::RecordDataReductionInit() { DCHECK(thread_checker_.CalledOnValidThread());
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.h b/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.h index c7f3685..213a248b 100644 --- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.h +++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.h
@@ -126,9 +126,7 @@ // Returns the event store being used. May be null if // InitDataReductionProxySettings has not been called. - DataReductionProxyEventStore* GetEventStore() const { - return event_store_; - } + DataReductionProxyEventStore* GetEventStore() const; // Returns true if the data reduction proxy configuration may be used. bool Allowed() const { @@ -253,9 +251,6 @@ PrefService* prefs_; - // The caller must ensure that the |event_store_| outlives this instance. - DataReductionProxyEventStore* event_store_; - // The caller must ensure that the |config_| outlives this instance. DataReductionProxyConfig* config_;
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings_unittest.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings_unittest.cc index 2b1a17b..dce4f0a 100644 --- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings_unittest.cc +++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings_unittest.cc
@@ -185,18 +185,6 @@ .SkipSettingsInitialization() .Build(); - // Enabling QUIC should have no effect since secure proxy should not - // use QUIC. If secure proxy check incorrectly uses QUIC, the tests will - // fail because Mock sockets do not speak QUIC. - scoped_ptr<net::HttpNetworkSession::Params> params( - new net::HttpNetworkSession::Params()); - params->use_alternate_protocols = true; - params->enable_quic = true; - params->origin_to_force_quic_on = net::HostPortPair::FromString( - TestDataReductionProxyParams::DefaultSecureProxyCheckURL()); - - context.set_http_network_session_params(params.Pass()); - context.set_net_log(drp_test_context->net_log()); net::MockClientSocketFactory mock_socket_factory; context.set_client_socket_factory(&mock_socket_factory);
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_tamper_detection.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_tamper_detection.cc index a9f75c42..823d0a7 100644 --- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_tamper_detection.cc +++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_tamper_detection.cc
@@ -109,15 +109,9 @@ } // Chrome-Proxy header has not been tampered with, and thus other - // fingerprints are valid. Reports the number of responses that other - // fingerprints will be checked. - REPORT_TAMPER_DETECTION_UMA( - scheme_is_https, - "DataReductionProxy.HeaderTamperDetectionHTTPS", - "DataReductionProxy.HeaderTamperDetectionHTTP", - carrier_id); - + // fingerprints are valid. bool tampered = false; + int64 original_content_length = -1; std::string fingerprint; if (GetDataReductionProxyActionFingerprintVia(headers, &fingerprint)) { @@ -140,7 +134,6 @@ if (GetDataReductionProxyActionFingerprintContentLength( headers, &fingerprint)) { - int64 original_content_length; if (tamper_detection.ValidateContentLength(fingerprint, content_length, &original_content_length)) { @@ -158,6 +151,10 @@ carrier_id); } + // Reports the number of responses that other fingerprints will be checked, + // separated by MIME type. + tamper_detection.ReportUMAForTamperDetectionCount(original_content_length); + return tampered; } @@ -174,6 +171,88 @@ DataReductionProxyTamperDetection::~DataReductionProxyTamperDetection() {}; +void DataReductionProxyTamperDetection::ReportUMAForTamperDetectionCount( + int64 original_content_length) const { + REPORT_TAMPER_DETECTION_UMA( + scheme_is_https_, "DataReductionProxy.HeaderTamperDetectionHTTPS", + "DataReductionProxy.HeaderTamperDetectionHTTP", carrier_id_); + + std::string mime_type; + response_headers_->GetMimeType(&mime_type); + + if (net::MatchesMimeType("text/javascript", mime_type) || + net::MatchesMimeType("application/x-javascript", mime_type) || + net::MatchesMimeType("application/javascript", mime_type)) { + REPORT_TAMPER_DETECTION_UMA( + scheme_is_https_, "DataReductionProxy.HeaderTamperDetectionHTTPS_JS", + "DataReductionProxy.HeaderTamperDetectionHTTP_JS", carrier_id_); + } else if (net::MatchesMimeType("text/css", mime_type)) { + REPORT_TAMPER_DETECTION_UMA( + scheme_is_https_, "DataReductionProxy.HeaderTamperDetectionHTTPS_CSS", + "DataReductionProxy.HeaderTamperDetectionHTTP_CSS", carrier_id_); + } else if (net::MatchesMimeType("image/*", mime_type)) { + REPORT_TAMPER_DETECTION_UMA( + scheme_is_https_, "DataReductionProxy.HeaderTamperDetectionHTTPS_Image", + "DataReductionProxy.HeaderTamperDetectionHTTP_Image", carrier_id_); + + if (net::MatchesMimeType("image/gif", mime_type)) { + REPORT_TAMPER_DETECTION_UMA( + scheme_is_https_, + "DataReductionProxy.HeaderTamperDetectionHTTPS_Image_GIF", + "DataReductionProxy.HeaderTamperDetectionHTTP_Image_GIF", + carrier_id_); + } else if (net::MatchesMimeType("image/jpeg", mime_type) || + net::MatchesMimeType("image/jpg", mime_type)) { + REPORT_TAMPER_DETECTION_UMA( + scheme_is_https_, + "DataReductionProxy.HeaderTamperDetectionHTTPS_Image_JPG", + "DataReductionProxy.HeaderTamperDetectionHTTP_Image_JPG", + carrier_id_); + } else if (net::MatchesMimeType("image/png", mime_type)) { + REPORT_TAMPER_DETECTION_UMA( + scheme_is_https_, + "DataReductionProxy.HeaderTamperDetectionHTTPS_Image_PNG", + "DataReductionProxy.HeaderTamperDetectionHTTP_Image_PNG", + carrier_id_); + } else if (net::MatchesMimeType("image/webp", mime_type)) { + REPORT_TAMPER_DETECTION_UMA( + scheme_is_https_, + "DataReductionProxy.HeaderTamperDetectionHTTPS_Image_WEBP", + "DataReductionProxy.HeaderTamperDetectionHTTP_Image_WEBP", + carrier_id_); + } + + if (original_content_length == -1) + return; + + if (original_content_length < 10 * 1024) { // 0-10KB + REPORT_TAMPER_DETECTION_UMA( + scheme_is_https_, + "DataReductionProxy.HeaderTamperDetectionHTTPS_Image_0_10KB", + "DataReductionProxy.HeaderTamperDetectionHTTP_Image_0_10KB", + carrier_id_); + } else if (original_content_length < 100 * 1024) { // 10-100KB + REPORT_TAMPER_DETECTION_UMA( + scheme_is_https_, + "DataReductionProxy.HeaderTamperDetectionHTTPS_Image_10_100KB", + "DataReductionProxy.HeaderTamperDetectionHTTP_Image_10_100KB", + carrier_id_); + } else if (original_content_length < 500 * 1024) { // 100-500KB + REPORT_TAMPER_DETECTION_UMA( + scheme_is_https_, + "DataReductionProxy.HeaderTamperDetectionHTTPS_Image_100_500KB", + "DataReductionProxy.HeaderTamperDetectionHTTP_Image_100_500KB", + carrier_id_); + } else { // >=500KB + REPORT_TAMPER_DETECTION_UMA( + scheme_is_https_, + "DataReductionProxy.HeaderTamperDetectionHTTPS_Image_500KB", + "DataReductionProxy.HeaderTamperDetectionHTTP_Image_500KB", + carrier_id_); + } + } +} + // |fingerprint| is Base64 encoded. Decodes it first. Then calculates the // fingerprint of received Chrome-Proxy header, and compares the two to see // whether they are equal or not.
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_tamper_detection.h b/components/data_reduction_proxy/core/browser/data_reduction_proxy_tamper_detection.h index 4f52412..2c5712b 100644 --- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_tamper_detection.h +++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_tamper_detection.h
@@ -101,8 +101,14 @@ FRIEND_TEST_ALL_PREFIXES(DataReductionProxyTamperDetectionTest, GetHeaderValues); FRIEND_TEST_ALL_PREFIXES(DataReductionProxyTamperDetectionTest, + HistogramCount); + FRIEND_TEST_ALL_PREFIXES(DataReductionProxyTamperDetectionTest, DetectAndReport); + // Reports UMA for the numbers of responses with valid fingerprints, separated + // by MIME type. + void ReportUMAForTamperDetectionCount(int64 original_content_length) const; + // Returns the result of validating Chrome-Proxy header. bool ValidateChromeProxyHeader(const std::string& fingerprint) const;
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_tamper_detection_unittest.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_tamper_detection_unittest.cc index 8023c31..cdda92c 100644 --- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_tamper_detection_unittest.cc +++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_tamper_detection_unittest.cc
@@ -14,6 +14,7 @@ #include "base/memory/scoped_ptr.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_split.h" +#include "base/test/histogram_tester.h" #include "components/data_reduction_proxy/core/common/data_reduction_proxy_headers.h" #include "components/data_reduction_proxy/core/common/data_reduction_proxy_headers_test_utils.h" #include "net/http/http_response_headers.h" @@ -594,6 +595,107 @@ } } +// Tests UMA histogram count. +TEST_F(DataReductionProxyTamperDetectionTest, HistogramCount) { + struct { + std::string raw_header; + std::string histogram_name_suffix; + int original_content_length; + std::string image_histogram_name_suffix; + } tests[] = { + // Checks the correctness of histogram for Javascript + {"HTTP/1.1 200 OK\n" + "Content-Type: text/javascript\n", + "_JS", + -1, + ""}, + // Checks the correctness of histogram for CSS + {"HTTP/1.1 200 OK\n" + "Content-Type: text/css\n", + "_CSS", + -1, + ""}, + // Checks the correctness of histogram for image + {"HTTP/1.1 200 OK\n" + "Content-Type: image/test\n", + "_Image", + 1, + "_Image_0_10KB"}, + // Checks the correctness of histogram for GIF + {"HTTP/1.1 200 OK\n" + "Content-Type: image/gif\n", + "_Image_GIF", + 20 * 1024, + "_Image_10_100KB"}, + // Checks the correctness of histogram for JPG + {"HTTP/1.1 200 OK\n" + "Content-Type: image/jpeg\n", + "_Image_JPG", + 200 * 1024, + "_Image_100_500KB"}, + // Checks the correctness of histogram for PNG + {"HTTP/1.1 200 OK\n" + "Content-Type: image/png\n", + "_Image_PNG", + 600 * 1024, + "_Image_500KB"}, + // Checks the correctness of histogram for WebP + {"HTTP/1.1 200 OK\n" + "Content-Type: image/webp\n", + "_Image_WEBP", + -1, + ""}, + }; + + const int carrier_id = 100; + + for (auto& test : tests) { + std::string raw_headers(test.raw_header); + HeadersToRaw(&raw_headers); + scoped_refptr<net::HttpResponseHeaders> headers( + new net::HttpResponseHeaders(raw_headers)); + + // Test HTTPS and HTTP separately. + int https_values[] = {true, false}; + for (auto https : https_values) { + base::HistogramTester histogram_tester; + + DataReductionProxyTamperDetection tamper_detection(headers.get(), https, + carrier_id); + tamper_detection.ReportUMAForTamperDetectionCount( + test.original_content_length); + histogram_tester.ExpectTotalCount( + std::string("DataReductionProxy.HeaderTamperDetectionHTTP") + + (https ? "S" : "") + test.histogram_name_suffix + "_Total", + 1); + histogram_tester.ExpectUniqueSample( + std::string("DataReductionProxy.HeaderTamperDetectionHTTP") + + (https ? "S" : "") + test.histogram_name_suffix, + carrier_id, 1); + histogram_tester.ExpectTotalCount( + std::string("DataReductionProxy.HeaderTamperDetectionHTTP") + + (https ? "S" : "") + "_Total", + 1); + histogram_tester.ExpectUniqueSample( + std::string("DataReductionProxy.HeaderTamperDetectionHTTP") + + (https ? "S" : ""), + carrier_id, 1); + + if (test.original_content_length != -1) { + histogram_tester.ExpectTotalCount( + std::string("DataReductionProxy.HeaderTamperDetectionHTTP") + + (https ? "S" : "") + test.image_histogram_name_suffix + + "_Total", + 1); + histogram_tester.ExpectUniqueSample( + std::string("DataReductionProxy.HeaderTamperDetectionHTTP") + + (https ? "S" : "") + test.image_histogram_name_suffix, + carrier_id, 1); + } + } + } +} + // Tests main function DetectAndReport. TEST_F(DataReductionProxyTamperDetectionTest, DetectAndReport) { struct {
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 b3c9c4a..f070f13 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
@@ -15,6 +15,8 @@ #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_network_delegate.h" #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_prefs.h" #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.h" +#include "components/data_reduction_proxy/core/common/data_reduction_proxy_event_creator.h" +#include "components/data_reduction_proxy/core/common/data_reduction_proxy_event_storage_delegate_test_utils.h" #include "components/data_reduction_proxy/core/common/data_reduction_proxy_event_store.h" #include "components/data_reduction_proxy/core/common/data_reduction_proxy_params.h" #include "components/data_reduction_proxy/core/common/data_reduction_proxy_params_test_utils.h" @@ -23,6 +25,7 @@ #include "net/url_request/url_request_intercepting_job_factory.h" #include "net/url_request/url_request_job_factory_impl.h" #include "net/url_request/url_request_test_util.h" +#include "url/gurl.h" namespace { @@ -71,11 +74,16 @@ now_offset_ = now_offset; } +const std::string& TestDataReductionProxyRequestOptions::GetSecureSession() + const { + return DataReductionProxyRequestOptions::GetSecureSession(); +} + MockDataReductionProxyRequestOptions::MockDataReductionProxyRequestOptions( Client client, const std::string& version, DataReductionProxyConfig* config) - : DataReductionProxyRequestOptions(client, version, config) { + : TestDataReductionProxyRequestOptions(client, version, config) { } MockDataReductionProxyRequestOptions::~MockDataReductionProxyRequestOptions() { @@ -113,6 +121,16 @@ return config_refresh_timer_.GetCurrentDelay(); } +int TestDataReductionProxyConfigServiceClient::GetBackoffErrorCount() { + return test_backoff_entry_.failure_count(); +} + +void TestDataReductionProxyConfigServiceClient::SetConfigServiceURL( + const GURL& service_url) { + config_service_url_ = service_url; + use_local_config_ = !config_service_url_.is_valid(); +} + base::Time TestDataReductionProxyConfigServiceClient::Now() { return tick_clock_.Now(); } @@ -157,7 +175,7 @@ TestDataReductionProxyIOData::TestDataReductionProxyIOData( scoped_refptr<base::SingleThreadTaskRunner> task_runner, scoped_ptr<DataReductionProxyConfig> config, - scoped_ptr<DataReductionProxyEventStore> event_store, + scoped_ptr<DataReductionProxyEventCreator> event_creator, scoped_ptr<DataReductionProxyRequestOptions> request_options, scoped_ptr<DataReductionProxyConfigurator> configurator, scoped_ptr<DataReductionProxyConfigServiceClient> config_client) @@ -165,7 +183,7 @@ io_task_runner_ = task_runner; ui_task_runner_ = task_runner; config_ = config.Pass(); - event_store_ = event_store.Pass(); + event_creator_ = event_creator.Pass(); request_options_ = request_options.Pass(); configurator_ = configurator.Pass(); config_client_ = config_client.Pass(); @@ -297,16 +315,18 @@ task_runner, test_request_context.Pass()); } - scoped_ptr<DataReductionProxyEventStore> event_store( - new DataReductionProxyEventStore(task_runner)); + scoped_ptr<TestDataReductionProxyEventStorageDelegate> storage_delegate( + new TestDataReductionProxyEventStorageDelegate()); + scoped_ptr<DataReductionProxyEventCreator> event_creator( + new DataReductionProxyEventCreator(storage_delegate.get())); scoped_ptr<DataReductionProxyConfigurator> configurator; if (use_test_configurator_) { test_context_flags |= USE_TEST_CONFIGURATOR; configurator.reset(new TestDataReductionProxyConfigurator( - net_log.get(), event_store.get())); + net_log.get(), event_creator.get())); } else { configurator.reset( - new DataReductionProxyConfigurator(net_log.get(), event_store.get())); + new DataReductionProxyConfigurator(net_log.get(), event_creator.get())); } scoped_ptr<TestDataReductionProxyConfig> config; @@ -322,16 +342,16 @@ raw_mutable_config = mutable_config.get(); config.reset(new TestDataReductionProxyConfig( mutable_config.Pass(), task_runner, net_log.get(), configurator.get(), - event_store.get())); + event_creator.get())); } else if (use_mock_config_) { test_context_flags |= USE_MOCK_CONFIG; config.reset(new MockDataReductionProxyConfig( params.Pass(), task_runner, net_log.get(), configurator.get(), - event_store.get())); + event_creator.get())); } else { config.reset(new TestDataReductionProxyConfig( params.Pass(), task_runner, net_log.get(), configurator.get(), - event_store.get())); + event_creator.get())); } scoped_ptr<DataReductionProxyRequestOptions> request_options; @@ -367,15 +387,17 @@ scoped_ptr<TestDataReductionProxyIOData> io_data( new TestDataReductionProxyIOData( - task_runner, config.Pass(), event_store.Pass(), + task_runner, config.Pass(), event_creator.Pass(), request_options.Pass(), configurator.Pass(), config_client.Pass())); io_data->InitOnUIThread(pref_service.get()); + io_data->SetSimpleURLRequestContextGetter(request_context_getter); scoped_ptr<DataReductionProxyTestContext> test_context( new DataReductionProxyTestContext( loop.Pass(), task_runner, pref_service.Pass(), net_log.Pass(), request_context_getter, mock_socket_factory_, io_data.Pass(), - settings.Pass(), raw_params, test_context_flags)); + settings.Pass(), storage_delegate.Pass(), raw_params, + test_context_flags)); if (!skip_settings_initialization_) test_context->InitSettingsWithoutCheck(); @@ -392,6 +414,7 @@ net::MockClientSocketFactory* mock_socket_factory, scoped_ptr<TestDataReductionProxyIOData> io_data, scoped_ptr<DataReductionProxySettings> settings, + scoped_ptr<TestDataReductionProxyEventStorageDelegate> storage_delegate, TestDataReductionProxyParams* params, unsigned int test_context_flags) : test_context_flags_(test_context_flags), @@ -403,6 +426,7 @@ mock_socket_factory_(mock_socket_factory), io_data_(io_data.Pass()), settings_(settings.Pass()), + storage_delegate_(storage_delegate.Pass()), params_(params) { } @@ -423,8 +447,13 @@ settings_->InitDataReductionProxySettings( simple_pref_service_.get(), io_data_.get(), CreateDataReductionProxyServiceInternal()); + storage_delegate_->SetStorageDelegate( + settings_->data_reduction_proxy_service()->event_store()); io_data_->SetDataReductionProxyService( settings_->data_reduction_proxy_service()->GetWeakPtr()); + if (io_data_->config_client()) + io_data_->config_client()->InitializeOnIOThread( + request_context_getter_.get()); settings_->data_reduction_proxy_service()->SetIOData(io_data_->GetWeakPtr()); }
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 b1d29d2..e5c4ca0 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
@@ -22,8 +22,10 @@ #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_settings_test_utils.h" #include "net/base/backoff_entry.h" #include "net/log/test_net_log.h" +#include "net/url_request/url_request_context_getter.h" #include "testing/gmock/include/gmock/gmock.h" +class GURL; class TestingPrefServiceSimple; namespace base { @@ -34,14 +36,13 @@ class MockClientSocketFactory; class NetLog; class URLRequestContext; -class URLRequestContextGetter; class URLRequestContextStorage; } namespace data_reduction_proxy { class DataReductionProxyConfigurator; -class DataReductionProxyEventStore; +class DataReductionProxyEventCreator; class DataReductionProxyMutableConfigValues; class DataReductionProxyRequestOptions; class DataReductionProxySettings; @@ -49,6 +50,7 @@ class MockDataReductionProxyConfig; class TestDataReductionProxyConfig; class TestDataReductionProxyConfigurator; +class TestDataReductionProxyEventStorageDelegate; class TestDataReductionProxyParams; // Test version of |DataReductionProxyRequestOptions|. @@ -67,13 +69,16 @@ // Time after the unix epoch that Now() reports. void set_offset(const base::TimeDelta& now_offset); + // Visible for testing. + const std::string& GetSecureSession() const override; + private: base::TimeDelta now_offset_; }; // Mock version of |DataReductionProxyRequestOptions|. class MockDataReductionProxyRequestOptions - : public DataReductionProxyRequestOptions { + : public TestDataReductionProxyRequestOptions { public: MockDataReductionProxyRequestOptions(Client client, const std::string& version, @@ -104,6 +109,10 @@ base::TimeDelta GetDelay() const; + int GetBackoffErrorCount(); + + void SetConfigServiceURL(const GURL& service_url); + protected: // Overrides of DataReductionProxyConfigServiceClient base::Time Now() override; @@ -155,7 +164,7 @@ TestDataReductionProxyIOData( scoped_refptr<base::SingleThreadTaskRunner> task_runner, scoped_ptr<DataReductionProxyConfig> config, - scoped_ptr<DataReductionProxyEventStore> event_store, + scoped_ptr<DataReductionProxyEventCreator> event_creator, scoped_ptr<DataReductionProxyRequestOptions> request_options, scoped_ptr<DataReductionProxyConfigurator> configurator, scoped_ptr<DataReductionProxyConfigServiceClient> config_client); @@ -169,6 +178,11 @@ return config_client_.get(); } + void SetSimpleURLRequestContextGetter( + const scoped_refptr<net::URLRequestContextGetter> context_getter) { + basic_url_request_context_getter_ = context_getter; + } + base::WeakPtr<DataReductionProxyIOData> GetWeakPtr() { return weak_factory_.GetWeakPtr(); } @@ -330,8 +344,8 @@ return request_context_getter_.get(); } - DataReductionProxyEventStore* event_store() const { - return io_data_->event_store(); + DataReductionProxyEventCreator* event_creator() const { + return io_data_->event_creator(); } DataReductionProxyConfigurator* configurator() const { @@ -380,6 +394,7 @@ net::MockClientSocketFactory* mock_socket_factory, scoped_ptr<TestDataReductionProxyIOData> io_data, scoped_ptr<DataReductionProxySettings> settings, + scoped_ptr<TestDataReductionProxyEventStorageDelegate> storage_delegate, TestDataReductionProxyParams* params, unsigned int test_context_flags); @@ -402,6 +417,7 @@ scoped_ptr<TestDataReductionProxyIOData> io_data_; scoped_ptr<DataReductionProxySettings> settings_; + scoped_ptr<TestDataReductionProxyEventStorageDelegate> storage_delegate_; TestDataReductionProxyParams* params_;
diff --git a/components/data_reduction_proxy/core/common/BUILD.gn b/components/data_reduction_proxy/core/common/BUILD.gn index 6976004..c0788de 100644 --- a/components/data_reduction_proxy/core/common/BUILD.gn +++ b/components/data_reduction_proxy/core/common/BUILD.gn
@@ -10,6 +10,9 @@ "data_reduction_proxy_client_config_parser.cc", "data_reduction_proxy_client_config_parser.h", "data_reduction_proxy_config_values.h", + "data_reduction_proxy_event_creator.cc", + "data_reduction_proxy_event_creator.h", + "data_reduction_proxy_event_storage_delegate.h", "data_reduction_proxy_event_store.cc", "data_reduction_proxy_event_store.h", "data_reduction_proxy_headers.cc", @@ -36,6 +39,8 @@ source_set("test_support") { testonly = true sources = [ + "data_reduction_proxy_event_storage_delegate_test_utils.cc", + "data_reduction_proxy_event_storage_delegate_test_utils.h", "data_reduction_proxy_headers_test_utils.cc", "data_reduction_proxy_headers_test_utils.h", "data_reduction_proxy_params_test_utils.cc",
diff --git a/components/data_reduction_proxy/core/common/data_reduction_proxy_event_creator.cc b/components/data_reduction_proxy/core/common/data_reduction_proxy_event_creator.cc new file mode 100644 index 0000000..1ce6246e --- /dev/null +++ b/components/data_reduction_proxy/core/common/data_reduction_proxy_event_creator.cc
@@ -0,0 +1,267 @@ +// 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 "components/data_reduction_proxy/core/common/data_reduction_proxy_event_creator.h" + +#include "base/bind.h" +#include "base/strings/string_number_conversions.h" +#include "base/time/time.h" +#include "base/values.h" +#include "net/proxy/proxy_server.h" + +namespace { + +scoped_ptr<base::Value> BuildDataReductionProxyEvent( + net::NetLog::EventType type, + const net::NetLog::Source& source, + net::NetLog::EventPhase phase, + const net::NetLog::ParametersCallback& parameters_callback) { + base::TimeTicks ticks_now = base::TimeTicks::Now(); + net::NetLog::EntryData entry_data(type, source, phase, ticks_now, + ¶meters_callback); + net::NetLog::Entry entry(&entry_data, + net::NetLogCaptureMode::IncludeSocketBytes()); + scoped_ptr<base::Value> entry_value(entry.ToValue()); + + return entry_value; +} + +int64 GetExpirationTicks(int bypass_seconds) { + base::TimeTicks expiration_ticks = + base::TimeTicks::Now() + base::TimeDelta::FromSeconds(bypass_seconds); + return (expiration_ticks - base::TimeTicks()).InMilliseconds(); +} + +// The following method creates a string resembling the output of +// net::ProxyServer::ToURI(). +std::string GetNormalizedProxyString(const std::string& proxy_origin) { + net::ProxyServer proxy_server = + net::ProxyServer::FromURI(proxy_origin, net::ProxyServer::SCHEME_HTTP); + if (proxy_server.is_valid()) + return proxy_origin; + + return std::string(); +} + +// A callback which creates a base::Value containing information about enabling +// the Data Reduction Proxy. Ownership of the base::Value is passed to the +// caller. +base::Value* EnableDataReductionProxyCallback( + bool primary_restricted, + bool fallback_restricted, + const std::string& primary_origin, + const std::string& fallback_origin, + const std::string& ssl_origin, + net::NetLogCaptureMode /* capture_mode */) { + base::DictionaryValue* dict = new base::DictionaryValue(); + dict->SetBoolean("enabled", true); + dict->SetBoolean("primary_restricted", primary_restricted); + dict->SetBoolean("fallback_restricted", fallback_restricted); + dict->SetString("primary_origin", GetNormalizedProxyString(primary_origin)); + dict->SetString("fallback_origin", GetNormalizedProxyString(fallback_origin)); + dict->SetString("ssl_origin", GetNormalizedProxyString(ssl_origin)); + return dict; +} + +// A callback which creates a base::Value containing information about disabling +// the Data Reduction Proxy. Ownership of the base::Value is passed to the +// caller. +base::Value* DisableDataReductionProxyCallback( + net::NetLogCaptureMode /* capture_mode */) { + base::DictionaryValue* dict = new base::DictionaryValue(); + dict->SetBoolean("enabled", false); + return dict; +} + +// A callback which creates a base::Value containing information about bypassing +// the Data Reduction Proxy. Ownership of the base::Value is passed to the +// caller. +base::Value* UrlBypassActionCallback( + const std::string& action, + const GURL& url, + int bypass_seconds, + int64 expiration_ticks, + net::NetLogCaptureMode /* capture_mode */) { + base::DictionaryValue* dict = new base::DictionaryValue(); + dict->SetString("action", action); + dict->SetString("url", url.spec()); + dict->SetString("bypass_duration_seconds", + base::Int64ToString(bypass_seconds)); + dict->SetString("expiration", base::Int64ToString(expiration_ticks)); + return dict; +} + +// A callback which creates a base::Value containing information about bypassing +// the Data Reduction Proxy. Ownership of the base::Value is passed to the +// caller. +base::Value* UrlBypassTypeCallback( + data_reduction_proxy::DataReductionProxyBypassType bypass_type, + const GURL& url, + int bypass_seconds, + int64 expiration_ticks, + net::NetLogCaptureMode /* capture_mode */) { + base::DictionaryValue* dict = new base::DictionaryValue(); + dict->SetInteger("bypass_type", bypass_type); + dict->SetString("url", url.spec()); + dict->SetString("bypass_duration_seconds", + base::Int64ToString(bypass_seconds)); + dict->SetString("expiration", base::Int64ToString(expiration_ticks)); + return dict; +} + +// A callback which creates a base::Value containing information about +// completing the Data Reduction Proxy secure proxy check. Ownership of the +// base::Value is passed to the caller. +base::Value* EndCanaryRequestCallback( + int net_error, + int http_response_code, + bool succeeded, + net::NetLogCaptureMode /* capture_mode */) { + base::DictionaryValue* dict = new base::DictionaryValue(); + dict->SetInteger("net_error", net_error); + dict->SetInteger("http_response_code", http_response_code); + dict->SetBoolean("check_succeeded", succeeded); + return dict; +} + +} // namespace + +namespace data_reduction_proxy { + +DataReductionProxyEventCreator::DataReductionProxyEventCreator( + DataReductionProxyEventStorageDelegate* storage_delegate) + : storage_delegate_(storage_delegate) { + DCHECK(storage_delegate); + // Constructed on the UI thread, but should be checked on the IO thread. + thread_checker_.DetachFromThread(); +} + +DataReductionProxyEventCreator::~DataReductionProxyEventCreator() { +} + +void DataReductionProxyEventCreator::AddProxyEnabledEvent( + net::NetLog* net_log, + bool primary_restricted, + bool fallback_restricted, + const std::string& primary_origin, + const std::string& fallback_origin, + const std::string& ssl_origin) { + DCHECK(thread_checker_.CalledOnValidThread()); + const net::NetLog::ParametersCallback& parameters_callback = base::Bind( + &EnableDataReductionProxyCallback, primary_restricted, + fallback_restricted, primary_origin, fallback_origin, ssl_origin); + PostEnabledEvent(net_log, net::NetLog::TYPE_DATA_REDUCTION_PROXY_ENABLED, + true, parameters_callback); +} + +void DataReductionProxyEventCreator::AddProxyDisabledEvent( + net::NetLog* net_log) { + DCHECK(thread_checker_.CalledOnValidThread()); + const net::NetLog::ParametersCallback& parameters_callback = + base::Bind(&DisableDataReductionProxyCallback); + PostEnabledEvent(net_log, net::NetLog::TYPE_DATA_REDUCTION_PROXY_ENABLED, + false, parameters_callback); +} + +void DataReductionProxyEventCreator::AddBypassActionEvent( + const net::BoundNetLog& net_log, + const std::string& bypass_action, + const GURL& url, + const base::TimeDelta& bypass_duration) { + DCHECK(thread_checker_.CalledOnValidThread()); + int64 expiration_ticks = GetExpirationTicks(bypass_duration.InSeconds()); + const net::NetLog::ParametersCallback& parameters_callback = + base::Bind(&UrlBypassActionCallback, bypass_action, url, + bypass_duration.InSeconds(), expiration_ticks); + PostBoundNetLogBypassEvent( + net_log, net::NetLog::TYPE_DATA_REDUCTION_PROXY_BYPASS_REQUESTED, + net::NetLog::PHASE_NONE, expiration_ticks, parameters_callback); +} + +void DataReductionProxyEventCreator::AddBypassTypeEvent( + const net::BoundNetLog& net_log, + DataReductionProxyBypassType bypass_type, + const GURL& url, + const base::TimeDelta& bypass_duration) { + DCHECK(thread_checker_.CalledOnValidThread()); + int64 expiration_ticks = GetExpirationTicks(bypass_duration.InSeconds()); + const net::NetLog::ParametersCallback& parameters_callback = + base::Bind(&UrlBypassTypeCallback, bypass_type, url, + bypass_duration.InSeconds(), expiration_ticks); + PostBoundNetLogBypassEvent( + net_log, net::NetLog::TYPE_DATA_REDUCTION_PROXY_BYPASS_REQUESTED, + net::NetLog::PHASE_NONE, expiration_ticks, parameters_callback); +} + +void DataReductionProxyEventCreator::BeginSecureProxyCheck( + const net::BoundNetLog& net_log, + const GURL& url) { + DCHECK(thread_checker_.CalledOnValidThread()); + // This callback must be invoked synchronously + const net::NetLog::ParametersCallback& parameters_callback = + net::NetLog::StringCallback("url", &url.spec()); + PostBoundNetLogSecureProxyCheckEvent( + net_log, net::NetLog::TYPE_DATA_REDUCTION_PROXY_CANARY_REQUEST, + net::NetLog::PHASE_BEGIN, + DataReductionProxyEventStorageDelegate::CHECK_PENDING, + parameters_callback); +} + +void DataReductionProxyEventCreator::EndSecureProxyCheck( + const net::BoundNetLog& net_log, + int net_error, + int http_response_code, + bool succeeded) { + DCHECK(thread_checker_.CalledOnValidThread()); + const net::NetLog::ParametersCallback& parameters_callback = base::Bind( + &EndCanaryRequestCallback, net_error, http_response_code, succeeded); + PostBoundNetLogSecureProxyCheckEvent( + net_log, net::NetLog::TYPE_DATA_REDUCTION_PROXY_CANARY_REQUEST, + net::NetLog::PHASE_END, + net_error == 0 ? DataReductionProxyEventStorageDelegate::CHECK_SUCCESS + : DataReductionProxyEventStorageDelegate::CHECK_FAILED, + parameters_callback); +} + +void DataReductionProxyEventCreator::PostEnabledEvent( + net::NetLog* net_log, + net::NetLog::EventType type, + bool enabled, + const net::NetLog::ParametersCallback& callback) { + scoped_ptr<base::Value> event = BuildDataReductionProxyEvent( + type, net::NetLog::Source(), net::NetLog::PHASE_NONE, callback); + if (event) + storage_delegate_->AddEnabledEvent(event.Pass(), enabled); + + if (net_log) + net_log->AddGlobalEntry(type, callback); +} + +void DataReductionProxyEventCreator::PostBoundNetLogBypassEvent( + const net::BoundNetLog& net_log, + net::NetLog::EventType type, + net::NetLog::EventPhase phase, + int64 expiration_ticks, + const net::NetLog::ParametersCallback& callback) { + scoped_ptr<base::Value> event = + BuildDataReductionProxyEvent(type, net_log.source(), phase, callback); + if (event) + storage_delegate_->AddAndSetLastBypassEvent(event.Pass(), expiration_ticks); + net_log.AddEntry(type, phase, callback); +} + +void DataReductionProxyEventCreator::PostBoundNetLogSecureProxyCheckEvent( + const net::BoundNetLog& net_log, + net::NetLog::EventType type, + net::NetLog::EventPhase phase, + DataReductionProxyEventStorageDelegate::SecureProxyCheckState state, + const net::NetLog::ParametersCallback& callback) { + scoped_ptr<base::Value> event( + BuildDataReductionProxyEvent(type, net_log.source(), phase, callback)); + if (event) + storage_delegate_->AddEventAndSecureProxyCheckState(event.Pass(), state); + net_log.AddEntry(type, phase, callback); +} + +} // namespace data_reduction_proxy
diff --git a/components/data_reduction_proxy/core/common/data_reduction_proxy_event_creator.h b/components/data_reduction_proxy/core/common/data_reduction_proxy_event_creator.h new file mode 100644 index 0000000..cd37e83 --- /dev/null +++ b/components/data_reduction_proxy/core/common/data_reduction_proxy_event_creator.h
@@ -0,0 +1,119 @@ +// 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 COMPONENTS_DATA_REDUCTION_PROXY_CORE_COMMON_DATA_REDUCTION_PROXY_EVENT_CREATOR_H_ +#define COMPONENTS_DATA_REDUCTION_PROXY_CORE_COMMON_DATA_REDUCTION_PROXY_EVENT_CREATOR_H_ + +#include <string> + +#include "base/basictypes.h" +#include "base/macros.h" +#include "base/memory/scoped_ptr.h" +#include "base/threading/thread_checker.h" +#include "components/data_reduction_proxy/core/common/data_reduction_proxy_event_storage_delegate.h" +#include "components/data_reduction_proxy/core/common/data_reduction_proxy_headers.h" +#include "net/log/net_log.h" + +class GURL; + +namespace base { +class TimeDelta; +class Value; +} + +namespace net { +class BoundNetLog; +} + +namespace data_reduction_proxy { + +// Central location for creating debug events for the Data Reduction Proxy. +// This object lives on the IO thread and all of its methods are expected to be +// called from there. +class DataReductionProxyEventCreator { + public: + // Constructs a DataReductionProxyEventCreator object. |storage_delegate| must + // outlive |this| and can be used to store Data Reduction Proxy events for + // debugging without requiring a net::NetLog. + explicit DataReductionProxyEventCreator( + DataReductionProxyEventStorageDelegate* storage_delegate); + + ~DataReductionProxyEventCreator(); + + // Adds the DATA_REDUCTION_PROXY_ENABLED event (with enabled=true) to the + // event store. + void AddProxyEnabledEvent(net::NetLog* net_log, + bool primary_restricted, + bool fallback_restricted, + const std::string& primary_origin, + const std::string& fallback_origin, + const std::string& ssl_origin); + + // Adds the DATA_REDUCTION_PROXY_ENABLED event (with enabled=false) to the + // event store. + void AddProxyDisabledEvent(net::NetLog* net_log); + + // Adds a DATA_REDUCTION_PROXY_BYPASS_REQUESTED event to the event store + // when the bypass reason is initiated by the data reduction proxy. + void AddBypassActionEvent(const net::BoundNetLog& net_log, + const std::string& bypass_action, + const GURL& gurl, + const base::TimeDelta& bypass_duration); + + // Adds a DATA_REDUCTION_PROXY_BYPASS_REQUESTED event to the event store + // when the bypass reason is not initiated by the data reduction proxy, such + // as network errors. + void AddBypassTypeEvent(const net::BoundNetLog& net_log, + DataReductionProxyBypassType bypass_type, + const GURL& gurl, + const base::TimeDelta& bypass_duration); + + // Adds a DATA_REDUCTION_PROXY_CANARY_REQUEST event to the event store + // when the secure proxy request has started. + void BeginSecureProxyCheck(const net::BoundNetLog& net_log, const GURL& gurl); + + // Adds a DATA_REDUCTION_PROXY_CANARY_REQUEST event to the event store + // when the secure proxy request has ended. + void EndSecureProxyCheck(const net::BoundNetLog& net_log, + int net_error, + int http_response_code, + bool succeeded); + + private: + // Prepare and post enabling/disabling proxy events for the event store on the + // a net::NetLog. + void PostEnabledEvent(net::NetLog* net_log, + net::NetLog::EventType type, + bool enable, + const net::NetLog::ParametersCallback& callback); + + // Prepare and post a Data Reduction Proxy bypass event for the event store + // on a BoundNetLog. + void PostBoundNetLogBypassEvent( + const net::BoundNetLog& net_log, + net::NetLog::EventType type, + net::NetLog::EventPhase phase, + int64 expiration_ticks, + const net::NetLog::ParametersCallback& callback); + + // Prepare and post a secure proxy check event for the event store on a + // BoundNetLog. + void PostBoundNetLogSecureProxyCheckEvent( + const net::BoundNetLog& net_log, + net::NetLog::EventType type, + net::NetLog::EventPhase phase, + DataReductionProxyEventStorageDelegate::SecureProxyCheckState state, + const net::NetLog::ParametersCallback& callback); + + // Must outlive |this|. Used for posting calls to the UI thread. + DataReductionProxyEventStorageDelegate* storage_delegate_; + + // Enforce usage on the IO thread. + base::ThreadChecker thread_checker_; + + DISALLOW_COPY_AND_ASSIGN(DataReductionProxyEventCreator); +}; + +} // namespace data_reduction_proxy +#endif // COMPONENTS_DATA_REDUCTION_PROXY_CORE_COMMON_DATA_REDUCTION_PROXY_EVENT_CREATOR_H_
diff --git a/components/data_reduction_proxy/core/common/data_reduction_proxy_event_storage_delegate.h b/components/data_reduction_proxy/core/common/data_reduction_proxy_event_storage_delegate.h new file mode 100644 index 0000000..d80bcaf --- /dev/null +++ b/components/data_reduction_proxy/core/common/data_reduction_proxy_event_storage_delegate.h
@@ -0,0 +1,42 @@ +// 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 COMPONENTS_DATA_REDUCTION_PROXY_CORE_COMMON_DATA_REDUCTION_PROXY_EVENT_STORAGE_DELEGATE_H_ +#define COMPONENTS_DATA_REDUCTION_PROXY_CORE_COMMON_DATA_REDUCTION_PROXY_EVENT_STORAGE_DELEGATE_H_ + +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" + +namespace base { +class Value; +} + +namespace data_reduction_proxy { + +// Defines an interface for storing Data Reduction Proxy events. +class DataReductionProxyEventStorageDelegate { + public: + enum SecureProxyCheckState { + CHECK_UNKNOWN, + CHECK_PENDING, + CHECK_SUCCESS, + CHECK_FAILED, + }; + + // Stores a DATA_REDUCTION_PROXY_ENABLED event. + virtual void AddEnabledEvent(scoped_ptr<base::Value> event, bool enabled) = 0; + + // Stores a DATA_REDUCTION_PROXY_BYPASS_REQUESTED event. + virtual void AddAndSetLastBypassEvent(scoped_ptr<base::Value> event, + int64 expiration_ticks) = 0; + + // Stores a DATA_REDUCTION_PROXY_CANARY_REQUEST event. + virtual void AddEventAndSecureProxyCheckState( + scoped_ptr<base::Value> event, + SecureProxyCheckState state) = 0; +}; + +} // namespace data_reduction_proxy + +#endif // COMPONENTS_DATA_REDUCTION_PROXY_CORE_COMMON_DATA_REDUCTION_PROXY_EVENT_STORAGE_DELEGATE_H_
diff --git a/components/data_reduction_proxy/core/common/data_reduction_proxy_event_storage_delegate_test_utils.cc b/components/data_reduction_proxy/core/common/data_reduction_proxy_event_storage_delegate_test_utils.cc new file mode 100644 index 0000000..a20a68e --- /dev/null +++ b/components/data_reduction_proxy/core/common/data_reduction_proxy_event_storage_delegate_test_utils.cc
@@ -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 "components/data_reduction_proxy/core/common/data_reduction_proxy_event_storage_delegate_test_utils.h" + +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" +#include "base/values.h" +#include "components/data_reduction_proxy/core/common/data_reduction_proxy_event_storage_delegate.h" + +namespace data_reduction_proxy { + +TestDataReductionProxyEventStorageDelegate:: + TestDataReductionProxyEventStorageDelegate() + : delegate_(nullptr) { +} + +TestDataReductionProxyEventStorageDelegate:: + ~TestDataReductionProxyEventStorageDelegate() { +} + +void TestDataReductionProxyEventStorageDelegate::SetStorageDelegate( + DataReductionProxyEventStorageDelegate* delegate) { + delegate_ = delegate; +} + +void TestDataReductionProxyEventStorageDelegate::AddEnabledEvent( + scoped_ptr<base::Value> event, + bool enabled) { + if (delegate_) + delegate_->AddEnabledEvent(event.Pass(), enabled); +} + +void TestDataReductionProxyEventStorageDelegate::AddAndSetLastBypassEvent( + scoped_ptr<base::Value> event, + int64 expiration_ticks) { + if (delegate_) + delegate_->AddAndSetLastBypassEvent(event.Pass(), expiration_ticks); +} + +void TestDataReductionProxyEventStorageDelegate:: + AddEventAndSecureProxyCheckState(scoped_ptr<base::Value> event, + SecureProxyCheckState state) { + if (delegate_) + delegate_->AddEventAndSecureProxyCheckState(event.Pass(), state); +} + +} // namespace data_reduction_proxy
diff --git a/components/data_reduction_proxy/core/common/data_reduction_proxy_event_storage_delegate_test_utils.h b/components/data_reduction_proxy/core/common/data_reduction_proxy_event_storage_delegate_test_utils.h new file mode 100644 index 0000000..65d3116 --- /dev/null +++ b/components/data_reduction_proxy/core/common/data_reduction_proxy_event_storage_delegate_test_utils.h
@@ -0,0 +1,44 @@ +// 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 COMPONENTS_DATA_REDUCTION_PROXY_CORE_COMMON_DATA_REDUCTION_PROXY_EVENT_STORE_TEST_UTILS_H_ +#define COMPONENTS_DATA_REDUCTION_PROXY_CORE_COMMON_DATA_REDUCTION_PROXY_EVENT_STORE_TEST_UTILS_H_ + +#include "components/data_reduction_proxy/core/common/data_reduction_proxy_event_storage_delegate.h" + +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" + +namespace base { +class Value; +} + +namespace data_reduction_proxy { + +class TestDataReductionProxyEventStorageDelegate + : public DataReductionProxyEventStorageDelegate { + public: + TestDataReductionProxyEventStorageDelegate(); + + virtual ~TestDataReductionProxyEventStorageDelegate(); + + // Sets |delegate_| at a later point in time. + void SetStorageDelegate(DataReductionProxyEventStorageDelegate* delegate); + + // Overrides of DataReductionProxyEventStorageDelegate: + void AddEnabledEvent(scoped_ptr<base::Value> event, bool enabled) override; + void AddAndSetLastBypassEvent(scoped_ptr<base::Value> event, + int64 expiration_ticks) override; + void AddEventAndSecureProxyCheckState(scoped_ptr<base::Value> event, + SecureProxyCheckState state) override; + + private: + // If not null, |this| will send DataReductionProxyEventStorageDelegate + // calls to |delegate_|. + DataReductionProxyEventStorageDelegate* delegate_; +}; + +} // namespace data_reduction_proxy + +#endif // COMPONENTS_DATA_REDUCTION_PROXY_CORE_COMMON_DATA_REDUCTION_PROXY_EVENT_STORE_TEST_UTILS_H_
diff --git a/components/data_reduction_proxy/core/common/data_reduction_proxy_event_store.cc b/components/data_reduction_proxy/core/common/data_reduction_proxy_event_store.cc index ef12af5..b0734b61 100644 --- a/components/data_reduction_proxy/core/common/data_reduction_proxy_event_store.cc +++ b/components/data_reduction_proxy/core/common/data_reduction_proxy_event_store.cc
@@ -5,19 +5,10 @@ #include "components/data_reduction_proxy/core/common/data_reduction_proxy_event_store.h" #include "base/basictypes.h" -#include "base/bind.h" -#include "base/location.h" -#include "base/memory/ref_counted.h" -#include "base/memory/scoped_ptr.h" -#include "base/single_thread_task_runner.h" #include "base/stl_util.h" -#include "base/strings/string_number_conversions.h" #include "base/time/time.h" #include "base/values.h" -#include "net/base/host_port_pair.h" -#include "net/log/net_log.h" -#include "net/proxy/proxy_server.h" -#include "url/gurl.h" +#include "components/data_reduction_proxy/core/common/data_reduction_proxy_event_storage_delegate.h" namespace { @@ -35,94 +26,6 @@ #undef BYPASS_EVENT_TYPE }; -scoped_ptr<base::Value> BuildDataReductionProxyEvent( - net::NetLog::EventType type, - const net::NetLog::Source& source, - net::NetLog::EventPhase phase, - const net::NetLog::ParametersCallback& parameters_callback) { - base::TimeTicks ticks_now = base::TimeTicks::Now(); - net::NetLog::EntryData entry_data( - type, source, phase, ticks_now, ¶meters_callback); - net::NetLog::Entry entry(&entry_data, net::NetLog::LOG_ALL); - scoped_ptr<base::Value> entry_value(entry.ToValue()); - - return entry_value; -} - -int64 GetExpirationTicks(int bypass_seconds) { - base::TimeTicks ticks_now = base::TimeTicks::Now(); - base::TimeTicks expiration_ticks = - ticks_now + base::TimeDelta::FromSeconds(bypass_seconds); - return (expiration_ticks - base::TimeTicks()).InMilliseconds(); -} - -// The following method creates a string resembling the output of -// net::ProxyServer::ToURI(). -std::string GetNormalizedProxyString(const std::string& proxy_origin) { - net::ProxyServer proxy_server = net::ProxyServer::FromURI( - proxy_origin, net::ProxyServer::SCHEME_HTTP); - if (proxy_server.is_valid()) - return proxy_origin; - else - return std::string(); -} - -// The following callbacks create a base::Value which contains information -// about various data reduction proxy events. Ownership of the base::Value is -// passed to the caller. -base::Value* EnableDataReductionProxyCallback( - bool primary_restricted, - bool fallback_restricted, - const std::string& primary_origin, - const std::string& fallback_origin, - const std::string& ssl_origin, - net::NetLog::LogLevel /* log_level */) { - base::DictionaryValue* dict = new base::DictionaryValue(); - dict->SetBoolean("enabled", true); - dict->SetBoolean("primary_restricted", primary_restricted); - dict->SetBoolean("fallback_restricted", fallback_restricted); - dict->SetString("primary_origin", GetNormalizedProxyString(primary_origin)); - dict->SetString("fallback_origin", GetNormalizedProxyString(fallback_origin)); - dict->SetString("ssl_origin", GetNormalizedProxyString(ssl_origin)); - return dict; -} - -base::Value* DisableDataReductionProxyCallback( - net::NetLog::LogLevel /* log_level */) { - base::DictionaryValue* dict = new base::DictionaryValue(); - dict->SetBoolean("enabled", false); - return dict; -} - -base::Value* UrlBypassActionCallback(const std::string& action, - const GURL& url, - int bypass_seconds, - int64 expiration_ticks, - net::NetLog::LogLevel /* log_level */) { - base::DictionaryValue* dict = new base::DictionaryValue(); - dict->SetString("action", action); - dict->SetString("url", url.spec()); - dict->SetString("bypass_duration_seconds", - base::Int64ToString(bypass_seconds)); - dict->SetString("expiration", base::Int64ToString(expiration_ticks)); - return dict; -} - -base::Value* UrlBypassTypeCallback( - data_reduction_proxy::DataReductionProxyBypassType bypass_type, - const GURL& url, - int bypass_seconds, - int64 expiration_ticks, - net::NetLog::LogLevel /* log_level */) { - base::DictionaryValue* dict = new base::DictionaryValue(); - dict->SetInteger("bypass_type", bypass_type); - dict->SetString("url", url.spec()); - dict->SetString("bypass_duration_seconds", - base::Int64ToString(bypass_seconds)); - dict->SetString("expiration", base::Int64ToString(expiration_ticks)); - return dict; -} - } // namespace namespace data_reduction_proxy { @@ -140,10 +43,8 @@ constants_dict->Set("dataReductionProxyBypassEventType", dict); } -DataReductionProxyEventStore::DataReductionProxyEventStore( - const scoped_refptr<base::SingleThreadTaskRunner>& ui_task_runner) - : ui_task_runner_(ui_task_runner), - enabled_(false), +DataReductionProxyEventStore::DataReductionProxyEventStore() + : enabled_(false), secure_proxy_check_state_(CHECK_UNKNOWN), expiration_ticks_(0) { } @@ -152,159 +53,8 @@ STLDeleteElements(&stored_events_); } -void DataReductionProxyEventStore::AddProxyEnabledEvent( - net::NetLog* net_log, - bool primary_restricted, - bool fallback_restricted, - const std::string& primary_origin, - const std::string& fallback_origin, - const std::string& ssl_origin) { - const net::NetLog::ParametersCallback& parameters_callback = - base::Bind(&EnableDataReductionProxyCallback, primary_restricted, - fallback_restricted, primary_origin, fallback_origin, - ssl_origin); - PostEnabledEvent(net_log, - net::NetLog::TYPE_DATA_REDUCTION_PROXY_ENABLED, - true, - parameters_callback); -} - -void DataReductionProxyEventStore::AddProxyDisabledEvent( - net::NetLog* net_log) { - const net::NetLog::ParametersCallback& parameters_callback = - base::Bind(&DisableDataReductionProxyCallback); - PostEnabledEvent(net_log, - net::NetLog::TYPE_DATA_REDUCTION_PROXY_ENABLED, - false, - parameters_callback); -} - -void DataReductionProxyEventStore::AddBypassActionEvent( - const net::BoundNetLog& net_log, - const std::string& bypass_action, - const GURL& url, - const base::TimeDelta& bypass_duration) { - int64 expiration_ticks = GetExpirationTicks(bypass_duration.InSeconds()); - const net::NetLog::ParametersCallback& parameters_callback = - base::Bind(&UrlBypassActionCallback, bypass_action, url, - bypass_duration.InSeconds(), expiration_ticks); - PostBoundNetLogBypassEvent( - net_log, - net::NetLog::TYPE_DATA_REDUCTION_PROXY_BYPASS_REQUESTED, - net::NetLog::PHASE_NONE, - expiration_ticks, - parameters_callback); -} - -void DataReductionProxyEventStore::AddBypassTypeEvent( - const net::BoundNetLog& net_log, - DataReductionProxyBypassType bypass_type, - const GURL& url, - const base::TimeDelta& bypass_duration) { - int64 expiration_ticks = GetExpirationTicks(bypass_duration.InSeconds()); - const net::NetLog::ParametersCallback& parameters_callback = - base::Bind(&UrlBypassTypeCallback, bypass_type, url, - bypass_duration.InSeconds(), expiration_ticks); - PostBoundNetLogBypassEvent( - net_log, - net::NetLog::TYPE_DATA_REDUCTION_PROXY_BYPASS_REQUESTED, - net::NetLog::PHASE_NONE, - expiration_ticks, - parameters_callback); -} - -void DataReductionProxyEventStore::BeginSecureProxyCheck( - const net::BoundNetLog& net_log, - const GURL& url) { - // This callback must be invoked synchronously - const net::NetLog::ParametersCallback& parameters_callback = - net::NetLog::StringCallback("url", &url.spec()); - PostBoundNetLogSecureProxyCheckEvent( - net_log, - net::NetLog::TYPE_DATA_REDUCTION_PROXY_CANARY_REQUEST, - net::NetLog::PHASE_BEGIN, - CHECK_PENDING, - parameters_callback); -} - -void DataReductionProxyEventStore::EndSecureProxyCheck( - const net::BoundNetLog& net_log, - int net_error) { - const net::NetLog::ParametersCallback& parameters_callback = - net::NetLog::IntegerCallback("net_error", net_error); - PostBoundNetLogSecureProxyCheckEvent( - net_log, - net::NetLog::TYPE_DATA_REDUCTION_PROXY_CANARY_REQUEST, - net::NetLog::PHASE_END, - net_error == 0 ? CHECK_SUCCESS : CHECK_FAILED, - parameters_callback); -} - -void DataReductionProxyEventStore::PostEnabledEvent( - net::NetLog* net_log, - net::NetLog::EventType type, - bool enabled, - const net::NetLog::ParametersCallback& callback) { - scoped_ptr<base::Value> event = BuildDataReductionProxyEvent( - type, net::NetLog::Source(), net::NetLog::PHASE_NONE, callback); - if (event.get()) { - ui_task_runner_->PostTask( - FROM_HERE, - base::Bind(&DataReductionProxyEventStore::AddEnabledEventOnUIThread, - base::Unretained(this), - base::Passed(&event), - enabled)); - } - - if (net_log) - net_log->AddGlobalEntry(type, callback); -} - -void DataReductionProxyEventStore::PostBoundNetLogBypassEvent( - const net::BoundNetLog& net_log, - net::NetLog::EventType type, - net::NetLog::EventPhase phase, - int64 expiration_ticks, - const net::NetLog::ParametersCallback& callback) { - scoped_ptr<base::Value> event = BuildDataReductionProxyEvent( - type, net_log.source(), phase, callback); - if (event.get()) { - ui_task_runner_->PostTask( - FROM_HERE, - base::Bind( - &DataReductionProxyEventStore::AddAndSetLastBypassEventOnUIThread, - base::Unretained(this), - base::Passed(&event), - expiration_ticks)); - } - - net_log.AddEntry(type, phase, callback); -} - -void DataReductionProxyEventStore::PostBoundNetLogSecureProxyCheckEvent( - const net::BoundNetLog& net_log, - net::NetLog::EventType type, - net::NetLog::EventPhase phase, - SecureProxyCheckState state, - const net::NetLog::ParametersCallback& callback) { - scoped_ptr<base::Value> event( - BuildDataReductionProxyEvent(type, net_log.source(), phase, callback)); - if (event.get()) { - ui_task_runner_->PostTask( - FROM_HERE, - base::Bind( - &DataReductionProxyEventStore:: - AddEventAndSecureProxyCheckStateOnUIThread, - base::Unretained(this), - base::Passed(&event), - state)); - } - net_log.AddEntry(type, phase, callback); -} - base::Value* DataReductionProxyEventStore::GetSummaryValue() const { - DCHECK(ui_task_runner_->BelongsToCurrentThread()); - + DCHECK(thread_checker_.CalledOnValidThread()); scoped_ptr<base::DictionaryValue> data_reduction_proxy_values( new base::DictionaryValue()); data_reduction_proxy_values->SetBoolean("enabled", enabled_); @@ -351,9 +101,7 @@ return data_reduction_proxy_values.release(); } -void DataReductionProxyEventStore::AddEventOnUIThread( - scoped_ptr<base::Value> entry) { - DCHECK(ui_task_runner_->BelongsToCurrentThread()); +void DataReductionProxyEventStore::AddEvent(scoped_ptr<base::Value> entry) { if (stored_events_.size() == kMaxEventsToStore) { base::Value* head = stored_events_.front(); stored_events_.pop_front(); @@ -363,33 +111,33 @@ stored_events_.push_back(entry.release()); } -void DataReductionProxyEventStore::AddEnabledEventOnUIThread( +void DataReductionProxyEventStore::AddEnabledEvent( scoped_ptr<base::Value> entry, bool enabled) { - DCHECK(ui_task_runner_->BelongsToCurrentThread()); + DCHECK(thread_checker_.CalledOnValidThread()); enabled_ = enabled; if (enabled) current_configuration_.reset(entry->DeepCopy()); else current_configuration_.reset(); - AddEventOnUIThread(entry.Pass()); + AddEvent(entry.Pass()); } -void DataReductionProxyEventStore::AddEventAndSecureProxyCheckStateOnUIThread( +void DataReductionProxyEventStore::AddEventAndSecureProxyCheckState( scoped_ptr<base::Value> entry, SecureProxyCheckState state) { - DCHECK(ui_task_runner_->BelongsToCurrentThread()); + DCHECK(thread_checker_.CalledOnValidThread()); secure_proxy_check_state_ = state; - AddEventOnUIThread(entry.Pass()); + AddEvent(entry.Pass()); } -void DataReductionProxyEventStore::AddAndSetLastBypassEventOnUIThread( +void DataReductionProxyEventStore::AddAndSetLastBypassEvent( scoped_ptr<base::Value> entry, int64 expiration_ticks) { - DCHECK(ui_task_runner_->BelongsToCurrentThread()); + DCHECK(thread_checker_.CalledOnValidThread()); last_bypass_event_.reset(entry->DeepCopy()); expiration_ticks_ = expiration_ticks; - AddEventOnUIThread(entry.Pass()); + AddEvent(entry.Pass()); } } // namespace data_reduction_proxy
diff --git a/components/data_reduction_proxy/core/common/data_reduction_proxy_event_store.h b/components/data_reduction_proxy/core/common/data_reduction_proxy_event_store.h index ff3e3ea..e55eb04 100644 --- a/components/data_reduction_proxy/core/common/data_reduction_proxy_event_store.h +++ b/components/data_reduction_proxy/core/common/data_reduction_proxy_event_store.h
@@ -6,89 +6,33 @@ #define COMPONENTS_DATA_REDUCTION_PROXY_CORE_COMMON_DATA_REDUCTION_PROXY_EVENT_STORE_H_ #include <deque> -#include <string> #include "base/basictypes.h" #include "base/gtest_prod_util.h" -#include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" -#include "base/values.h" +#include "base/threading/thread_checker.h" +#include "components/data_reduction_proxy/core/common/data_reduction_proxy_event_storage_delegate.h" #include "components/data_reduction_proxy/core/common/data_reduction_proxy_headers.h" -#include "net/log/net_log.h" - -class GURL; namespace base { -class SingleThreadTaskRunner; +class DictionaryValue; class TimeDelta; class Value; } -namespace net { -class BoundNetLog; -class NetLog; -} - namespace data_reduction_proxy { -enum SecureProxyCheckState { - CHECK_UNKNOWN, - CHECK_PENDING, - CHECK_SUCCESS, - CHECK_FAILED, -}; - -class DataReductionProxyEventStore { +class DataReductionProxyEventStore + : public DataReductionProxyEventStorageDelegate { public: // Adds data reduction proxy specific constants to the net_internals // constants dictionary. static void AddConstants(base::DictionaryValue* constants_dict); - // Constructs a DataReductionProxyEventStore object with the given UI - // task runner. - explicit DataReductionProxyEventStore( - const scoped_refptr<base::SingleThreadTaskRunner>& ui_task_runner); + // Constructs a DataReductionProxyEventStore object + explicit DataReductionProxyEventStore(); - ~DataReductionProxyEventStore(); - - // Adds the DATA_REDUCTION_PROXY_ENABLED event (with enabled=true) to the - // event store. - void AddProxyEnabledEvent( - net::NetLog* net_log, - bool primary_restricted, - bool fallback_restricted, - const std::string& primary_origin, - const std::string& fallback_origin, - const std::string& ssl_origin); - - // Adds the DATA_REDUCTION_PROXY_ENABLED event (with enabled=false) to the - // event store. - void AddProxyDisabledEvent(net::NetLog* net_log); - - // Adds a DATA_REDUCTION_PROXY_BYPASS_REQUESTED event to the event store - // when the bypass reason is initiated by the data reduction proxy. - void AddBypassActionEvent( - const net::BoundNetLog& net_log, - const std::string& bypass_action, - const GURL& gurl, - const base::TimeDelta& bypass_duration); - - // Adds a DATA_REDUCTION_PROXY_BYPASS_REQUESTED event to the event store - // when the bypass reason is not initiated by the data reduction proxy, such - // as network errors. - void AddBypassTypeEvent( - const net::BoundNetLog& net_log, - DataReductionProxyBypassType bypass_type, - const GURL& gurl, - const base::TimeDelta& bypass_duration); - - // Adds a DATA_REDUCTION_PROXY_CANARY_REQUEST event to the event store - // when the secure proxy request has started. - void BeginSecureProxyCheck(const net::BoundNetLog& net_log, const GURL& gurl); - - // Adds a DATA_REDUCTION_PROXY_CANARY_REQUEST event to the event store - // when the secure proxy request has ended. - void EndSecureProxyCheck(const net::BoundNetLog& net_log, int net_error); + virtual ~DataReductionProxyEventStore(); // Creates a Value summary of Data Reduction Proxy related information: // - Whether the proxy is enabled @@ -98,6 +42,22 @@ // The caller is responsible for deleting the returned value. base::Value* GetSummaryValue() const; + // Override of DataReductionProxyEventStorageDelegate. + // Put |entry| on the deque of stored events and set |current_configuration_|. + void AddEnabledEvent(scoped_ptr<base::Value> entry, bool enabled) override; + + // Override of DataReductionProxyEventStorageDelegate. + // Put |entry| on a deque of events to store and set + // |secure_proxy_check_state_| + void AddEventAndSecureProxyCheckState(scoped_ptr<base::Value> entry, + SecureProxyCheckState state) override; + + // Override of DataReductionProxyEventStorageDelegate. + // Put |entry| on a deque of events to store and set |last_bypass_event_| and + // |expiration_ticks_| + void AddAndSetLastBypassEvent(scoped_ptr<base::Value> entry, + int64 expiration_ticks) override; + private: friend class DataReductionProxyEventStoreTest; FRIEND_TEST_ALL_PREFIXES(DataReductionProxyEventStoreTest, @@ -113,50 +73,9 @@ FRIEND_TEST_ALL_PREFIXES(DataReductionProxyEventStoreTest, TestEndSecureProxyCheck); - // Prepare and post enabling/disabling proxy events for the event_store on the - // global net_log. - void PostEnabledEvent(net::NetLog* net_log, - net::NetLog::EventType type, - bool enable, - const net::NetLog::ParametersCallback& callback); - - // Prepare and post a Data Reduction Proxy bypass event for the event_store - // on a BoundNetLog. - void PostBoundNetLogBypassEvent( - const net::BoundNetLog& net_log, - net::NetLog::EventType type, - net::NetLog::EventPhase phase, - int64 expiration_ticks, - const net::NetLog::ParametersCallback& callback); - - // Prepare and post a secure proxy check event for the event_store on a - // BoundNetLog. - void PostBoundNetLogSecureProxyCheckEvent( - const net::BoundNetLog& net_log, - net::NetLog::EventType type, - net::NetLog::EventPhase phase, - SecureProxyCheckState state, - const net::NetLog::ParametersCallback& callback); - // Put |entry| on a deque of events to store - void AddEventOnUIThread(scoped_ptr<base::Value> entry); + void AddEvent(scoped_ptr<base::Value> entry); - // Put |entry| on the deque of stored events and set |current_configuration_|. - void AddEnabledEventOnUIThread(scoped_ptr<base::Value> entry, bool enabled); - - // Put |entry| on a deque of events to store and set - // |secure_proxy_check_state_| - void AddEventAndSecureProxyCheckStateOnUIThread(scoped_ptr<base::Value> entry, - SecureProxyCheckState state); - - // Put |entry| on a deque of events to store and set |last_bypass_event_| and - // |expiration_ticks_| - void AddAndSetLastBypassEventOnUIThread(scoped_ptr<base::Value> entry, - int64 expiration_ticks); - - // A task runner to ensure that all reads/writes to |stored_events_| takes - // place on the UI thread. - scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner_; // A deque of data reduction proxy related events. It is used as a circular // buffer to prevent unbounded memory utilization. std::deque<base::Value*> stored_events_; @@ -171,6 +90,9 @@ // The expiration time of the |last_bypass_event_|. int64 expiration_ticks_; + // Enforce usage on the UI thread. + base::ThreadChecker thread_checker_; + DISALLOW_COPY_AND_ASSIGN(DataReductionProxyEventStore); };
diff --git a/components/data_reduction_proxy/core/common/data_reduction_proxy_event_store_unittest.cc b/components/data_reduction_proxy/core/common/data_reduction_proxy_event_store_unittest.cc index 6f266dd..05c996bb 100644 --- a/components/data_reduction_proxy/core/common/data_reduction_proxy_event_store_unittest.cc +++ b/components/data_reduction_proxy/core/common/data_reduction_proxy_event_store_unittest.cc
@@ -5,11 +5,12 @@ #include "components/data_reduction_proxy/core/common/data_reduction_proxy_event_store.h" #include "base/bind.h" -#include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" -#include "base/strings/string_number_conversions.h" -#include "base/test/test_simple_task_runner.h" +#include "base/time/time.h" +#include "base/values.h" +#include "components/data_reduction_proxy/core/common/data_reduction_proxy_event_creator.h" #include "components/data_reduction_proxy/core/common/data_reduction_proxy_params_test_utils.h" +#include "net/http/http_status_code.h" #include "net/log/net_log.h" #include "net/log/test_net_log.h" #include "testing/gtest/include/gtest/gtest.h" @@ -18,16 +19,15 @@ class DataReductionProxyEventStoreTest : public testing::Test { public: - DataReductionProxyEventStoreTest() - : task_runner_(scoped_refptr<base::TestSimpleTaskRunner>( - new base::TestSimpleTaskRunner())), - net_log_(new net::TestNetLog()) { + DataReductionProxyEventStoreTest() : net_log_(new net::TestNetLog()) { bound_net_log_ = net::BoundNetLog::Make( net_log_.get(), net::NetLog::SOURCE_DATA_REDUCTION_PROXY); } void SetUp() override { - proxy_.reset(new DataReductionProxyEventStore(task_runner_)); + event_store_.reset(new DataReductionProxyEventStore()); + event_creator_.reset( + new DataReductionProxyEventCreator(event_store_.get())); } net::TestNetLog::CapturedEntry GetSingleEntry() const { @@ -37,12 +37,10 @@ return entries[0]; } - DataReductionProxyEventStore* proxy() { - return proxy_.get(); - } + DataReductionProxyEventStore* event_store() { return event_store_.get(); } - base::TestSimpleTaskRunner* task_runner() { - return task_runner_.get(); + DataReductionProxyEventCreator* event_creator() { + return event_creator_.get(); } net::TestNetLog* net_log() { return net_log_.get(); } @@ -52,87 +50,84 @@ } private: - scoped_refptr<base::TestSimpleTaskRunner> task_runner_; scoped_ptr<net::TestNetLog> net_log_; - scoped_ptr<DataReductionProxyEventStore> proxy_; + scoped_ptr<DataReductionProxyEventStore> event_store_; + scoped_ptr<DataReductionProxyEventCreator> event_creator_; net::BoundNetLog bound_net_log_; }; TEST_F(DataReductionProxyEventStoreTest, TestAddProxyEnabledEvent) { - EXPECT_EQ(0u, proxy()->stored_events_.size()); - proxy()->AddProxyEnabledEvent( - net_log(), false, false, - TestDataReductionProxyParams::DefaultOrigin(), + EXPECT_EQ(0u, event_store()->stored_events_.size()); + event_creator()->AddProxyEnabledEvent( + net_log(), false, false, TestDataReductionProxyParams::DefaultOrigin(), TestDataReductionProxyParams::DefaultFallbackOrigin(), TestDataReductionProxyParams::DefaultSSLOrigin()); - task_runner()->RunPendingTasks(); - EXPECT_EQ(1u, proxy()->stored_events_.size()); + EXPECT_EQ(1u, event_store()->stored_events_.size()); net::TestNetLog::CapturedEntry entry = GetSingleEntry(); EXPECT_EQ(net::NetLog::TYPE_DATA_REDUCTION_PROXY_ENABLED, entry.type); } TEST_F(DataReductionProxyEventStoreTest, TestAddProxyDisabledEvent) { - EXPECT_EQ(0u, proxy()->stored_events_.size()); - proxy()->AddProxyDisabledEvent(net_log()); - task_runner()->RunPendingTasks(); - EXPECT_EQ(1u, proxy()->stored_events_.size()); + EXPECT_EQ(0u, event_store()->stored_events_.size()); + event_creator()->AddProxyDisabledEvent(net_log()); + EXPECT_EQ(1u, event_store()->stored_events_.size()); net::TestNetLog::CapturedEntry entry = GetSingleEntry(); EXPECT_EQ(net::NetLog::TYPE_DATA_REDUCTION_PROXY_ENABLED, entry.type); } TEST_F(DataReductionProxyEventStoreTest, TestAddBypassActionEvent) { - EXPECT_EQ(0u, proxy()->stored_events_.size()); - EXPECT_EQ(nullptr, proxy()->last_bypass_event_.get()); - proxy()->AddBypassActionEvent(bound_net_log(), "bypass", GURL(), - base::TimeDelta::FromMinutes(1)); - task_runner()->RunPendingTasks(); - EXPECT_EQ(1u, proxy()->stored_events_.size()); + EXPECT_EQ(0u, event_store()->stored_events_.size()); + EXPECT_EQ(nullptr, event_store()->last_bypass_event_.get()); + event_creator()->AddBypassActionEvent(bound_net_log(), "bypass", GURL(), + base::TimeDelta::FromMinutes(1)); + EXPECT_EQ(1u, event_store()->stored_events_.size()); net::TestNetLog::CapturedEntry entry = GetSingleEntry(); EXPECT_EQ(net::NetLog::TYPE_DATA_REDUCTION_PROXY_BYPASS_REQUESTED, entry.type); - EXPECT_NE(nullptr, proxy()->last_bypass_event_.get()); + EXPECT_NE(nullptr, event_store()->last_bypass_event_.get()); } TEST_F(DataReductionProxyEventStoreTest, TestAddBypassTypeEvent) { - EXPECT_EQ(0u, proxy()->stored_events_.size()); - EXPECT_EQ(nullptr, proxy()->last_bypass_event_.get()); - proxy()->AddBypassTypeEvent(bound_net_log(), BYPASS_EVENT_TYPE_LONG, GURL(), - base::TimeDelta::FromMinutes(1)); - task_runner()->RunPendingTasks(); - EXPECT_EQ(1u, proxy()->stored_events_.size()); + EXPECT_EQ(0u, event_store()->stored_events_.size()); + EXPECT_EQ(nullptr, event_store()->last_bypass_event_.get()); + event_creator()->AddBypassTypeEvent(bound_net_log(), BYPASS_EVENT_TYPE_LONG, + GURL(), base::TimeDelta::FromMinutes(1)); + EXPECT_EQ(1u, event_store()->stored_events_.size()); EXPECT_EQ(1u, net_log()->GetSize()); net::TestNetLog::CapturedEntry entry = GetSingleEntry(); EXPECT_EQ(net::NetLog::TYPE_DATA_REDUCTION_PROXY_BYPASS_REQUESTED, entry.type); - EXPECT_NE(nullptr, proxy()->last_bypass_event_.get()); + EXPECT_NE(nullptr, event_store()->last_bypass_event_.get()); } TEST_F(DataReductionProxyEventStoreTest, TestBeginSecureProxyCheck) { - EXPECT_EQ(0u, proxy()->stored_events_.size()); - EXPECT_EQ(CHECK_UNKNOWN, proxy()->secure_proxy_check_state_); - proxy()->BeginSecureProxyCheck(bound_net_log(), GURL()); - task_runner()->RunPendingTasks(); - EXPECT_EQ(1u, proxy()->stored_events_.size()); + EXPECT_EQ(0u, event_store()->stored_events_.size()); + EXPECT_EQ(DataReductionProxyEventStorageDelegate::CHECK_UNKNOWN, + event_store()->secure_proxy_check_state_); + event_creator()->BeginSecureProxyCheck(bound_net_log(), GURL()); + EXPECT_EQ(1u, event_store()->stored_events_.size()); EXPECT_EQ(1u, net_log()->GetSize()); net::TestNetLog::CapturedEntry entry = GetSingleEntry(); EXPECT_EQ(net::NetLog::TYPE_DATA_REDUCTION_PROXY_CANARY_REQUEST, entry.type); - EXPECT_EQ(CHECK_PENDING, proxy()->secure_proxy_check_state_); + EXPECT_EQ(DataReductionProxyEventStorageDelegate::CHECK_PENDING, + event_store()->secure_proxy_check_state_); } TEST_F(DataReductionProxyEventStoreTest, TestEndSecureProxyCheck) { - EXPECT_EQ(0u, proxy()->stored_events_.size()); - EXPECT_EQ(CHECK_UNKNOWN, proxy()->secure_proxy_check_state_); - proxy()->EndSecureProxyCheck(bound_net_log(), 0); - task_runner()->RunPendingTasks(); - EXPECT_EQ(1u, proxy()->stored_events_.size()); + EXPECT_EQ(0u, event_store()->stored_events_.size()); + EXPECT_EQ(DataReductionProxyEventStorageDelegate::CHECK_UNKNOWN, + event_store()->secure_proxy_check_state_); + event_creator()->EndSecureProxyCheck(bound_net_log(), 0, net::HTTP_OK, true); + EXPECT_EQ(1u, event_store()->stored_events_.size()); EXPECT_EQ(1u, net_log()->GetSize()); net::TestNetLog::CapturedEntry entry = GetSingleEntry(); EXPECT_EQ(net::NetLog::TYPE_DATA_REDUCTION_PROXY_CANARY_REQUEST, entry.type); - EXPECT_EQ(CHECK_SUCCESS, proxy()->secure_proxy_check_state_); + EXPECT_EQ(DataReductionProxyEventStorageDelegate::CHECK_SUCCESS, + event_store()->secure_proxy_check_state_); } } // namespace data_reduction_proxy
diff --git a/components/data_reduction_proxy/core/common/data_reduction_proxy_headers.cc b/components/data_reduction_proxy/core/common/data_reduction_proxy_headers.cc index e555cff..12e4c768 100644 --- a/components/data_reduction_proxy/core/common/data_reduction_proxy_headers.cc +++ b/components/data_reduction_proxy/core/common/data_reduction_proxy_headers.cc
@@ -12,7 +12,7 @@ #include "base/strings/string_piece.h" #include "base/strings/string_util.h" #include "base/time/time.h" -#include "components/data_reduction_proxy/core/common/data_reduction_proxy_event_store.h" +#include "components/data_reduction_proxy/core/common/data_reduction_proxy_event_creator.h" #include "net/http/http_response_headers.h" #include "net/http/http_status_code.h" @@ -111,13 +111,14 @@ return false; } -bool ParseHeadersAndSetProxyInfo(const net::HttpResponseHeaders* headers, - const GURL& url, - const net::BoundNetLog& bound_net_log, - DataReductionProxyInfo* proxy_info, - DataReductionProxyEventStore* event_store) { +bool ParseHeadersAndSetProxyInfo( + const net::HttpResponseHeaders* headers, + const GURL& url, + const net::BoundNetLog& bound_net_log, + DataReductionProxyInfo* proxy_info, + DataReductionProxyEventCreator* event_creator) { DCHECK(proxy_info); - DCHECK(event_store); + DCHECK(event_creator); // Support header of the form Chrome-Proxy: bypass|block=<duration>, where // <duration> is the number of seconds to wait before retrying @@ -134,8 +135,8 @@ headers, kChromeProxyActionBlock, &proxy_info->bypass_duration)) { proxy_info->bypass_all = true; proxy_info->mark_proxies_as_bad = true; - event_store->AddBypassActionEvent(bound_net_log, kChromeProxyActionBlock, - url, proxy_info->bypass_duration); + event_creator->AddBypassActionEvent(bound_net_log, kChromeProxyActionBlock, + url, proxy_info->bypass_duration); return true; } @@ -144,8 +145,8 @@ headers, kChromeProxyActionBypass, &proxy_info->bypass_duration)) { proxy_info->bypass_all = false; proxy_info->mark_proxies_as_bad = true; - event_store->AddBypassActionEvent(bound_net_log, kChromeProxyActionBypass, - url, proxy_info->bypass_duration); + event_creator->AddBypassActionEvent(bound_net_log, kChromeProxyActionBypass, + url, proxy_info->bypass_duration); return true; } @@ -159,9 +160,9 @@ proxy_info->bypass_all = true; proxy_info->mark_proxies_as_bad = false; proxy_info->bypass_duration = TimeDelta(); - event_store->AddBypassActionEvent(bound_net_log, - kChromeProxyActionBlockOnce, url, - proxy_info->bypass_duration); + event_creator->AddBypassActionEvent(bound_net_log, + kChromeProxyActionBlockOnce, url, + proxy_info->bypass_duration); return true; } @@ -198,14 +199,11 @@ const GURL& url, const net::BoundNetLog& bound_net_log, DataReductionProxyInfo* data_reduction_proxy_info, - DataReductionProxyEventStore* event_store, + DataReductionProxyEventCreator* event_creator, bool* event_logged) { DCHECK(data_reduction_proxy_info); - if (ParseHeadersAndSetProxyInfo(headers, - url, - bound_net_log, - data_reduction_proxy_info, - event_store)) { + if (ParseHeadersAndSetProxyInfo(headers, url, bound_net_log, + data_reduction_proxy_info, event_creator)) { *event_logged = true; // A chrome-proxy response header is only present in a 502. For proper
diff --git a/components/data_reduction_proxy/core/common/data_reduction_proxy_headers.h b/components/data_reduction_proxy/core/common/data_reduction_proxy_headers.h index 3df14963..3d37778 100644 --- a/components/data_reduction_proxy/core/common/data_reduction_proxy_headers.h +++ b/components/data_reduction_proxy/core/common/data_reduction_proxy_headers.h
@@ -21,7 +21,7 @@ namespace data_reduction_proxy { -class DataReductionProxyEventStore; +class DataReductionProxyEventCreator; // Values of the UMA DataReductionProxy.BypassType{Primary|Fallback} and // DataReductionProxy.BlockType{Primary|Fallback} histograms. This enum must @@ -61,7 +61,7 @@ const GURL& url, const net::BoundNetLog& bound_net_log, DataReductionProxyInfo* proxy_info, - DataReductionProxyEventStore* event_store); + DataReductionProxyEventCreator* event_creator); // Returns true if the response contains the data reduction proxy Via header // value. If non-NULL, sets |has_intermediary| to true if another server added @@ -79,7 +79,7 @@ const GURL& url, const net::BoundNetLog& bound_net_log, DataReductionProxyInfo* proxy_info, - DataReductionProxyEventStore* event_store, + DataReductionProxyEventCreator* event_creator, bool* event_logged); // Searches for the specified Chrome-Proxy action, and if present saves its
diff --git a/components/data_reduction_proxy/core/common/data_reduction_proxy_headers_unittest.cc b/components/data_reduction_proxy/core/common/data_reduction_proxy_headers_unittest.cc index cda2159c..460459a 100644 --- a/components/data_reduction_proxy/core/common/data_reduction_proxy_headers_unittest.cc +++ b/components/data_reduction_proxy/core/common/data_reduction_proxy_headers_unittest.cc
@@ -6,8 +6,8 @@ #include <vector> -#include "base/test/test_simple_task_runner.h" -#include "components/data_reduction_proxy/core/common/data_reduction_proxy_event_store.h" +#include "components/data_reduction_proxy/core/common/data_reduction_proxy_event_creator.h" +#include "components/data_reduction_proxy/core/common/data_reduction_proxy_event_storage_delegate_test_utils.h" #include "components/data_reduction_proxy/core/common/data_reduction_proxy_headers_test_utils.h" #include "net/http/http_response_headers.h" #include "net/proxy/proxy_service.h" @@ -16,17 +16,20 @@ namespace data_reduction_proxy { class DataReductionProxyHeadersTest : public testing::Test { - public: - DataReductionProxyHeadersTest() - : task_runner_(scoped_refptr<base::TestSimpleTaskRunner>( - new base::TestSimpleTaskRunner())) {} - + protected: void SetUp() override { - event_store_.reset(new DataReductionProxyEventStore(task_runner_)); + storage_delegate_.reset(new TestDataReductionProxyEventStorageDelegate()); + event_creator_.reset( + new DataReductionProxyEventCreator(storage_delegate_.get())); } - scoped_refptr<base::TestSimpleTaskRunner> task_runner_; - scoped_ptr<DataReductionProxyEventStore> event_store_; + DataReductionProxyEventCreator* event_creator() const { + return event_creator_.get(); + } + + private: + scoped_ptr<DataReductionProxyEventCreator> event_creator_; + scoped_ptr<TestDataReductionProxyEventStorageDelegate> storage_delegate_; }; TEST_F(DataReductionProxyHeadersTest, GetDataReductionProxyActionValue) { @@ -364,13 +367,10 @@ new net::HttpResponseHeaders(headers)); DataReductionProxyInfo data_reduction_proxy_info; - EXPECT_EQ( - tests[i].expected_result, - ParseHeadersAndSetProxyInfo(parsed.get(), - GURL(), - net::BoundNetLog(), - &data_reduction_proxy_info, - event_store_.get())); + EXPECT_EQ(tests[i].expected_result, + ParseHeadersAndSetProxyInfo( + parsed.get(), GURL(), net::BoundNetLog(), + &data_reduction_proxy_info, event_creator())); EXPECT_EQ(tests[i].expected_retry_delay, data_reduction_proxy_info.bypass_duration.InSeconds()); EXPECT_EQ(tests[i].expected_bypass_all, @@ -392,11 +392,8 @@ DataReductionProxyInfo data_reduction_proxy_info; EXPECT_TRUE( - ParseHeadersAndSetProxyInfo(parsed.get(), - GURL(), - net::BoundNetLog(), - &data_reduction_proxy_info, - event_store_.get())); + ParseHeadersAndSetProxyInfo(parsed.get(), GURL(), net::BoundNetLog(), + &data_reduction_proxy_info, event_creator())); EXPECT_LE(60, data_reduction_proxy_info.bypass_duration.InSeconds()); EXPECT_GE(5 * 60, data_reduction_proxy_info.bypass_duration.InSeconds()); EXPECT_FALSE(data_reduction_proxy_info.bypass_all); @@ -608,15 +605,10 @@ new net::HttpResponseHeaders(headers)); DataReductionProxyInfo chrome_proxy_info; bool event_was_logged; - EXPECT_EQ( - tests[i].expected_result, - GetDataReductionProxyBypassType( - parsed.get(), - GURL(), - net::BoundNetLog(), - &chrome_proxy_info, - event_store_.get(), - &event_was_logged)); + EXPECT_EQ(tests[i].expected_result, + GetDataReductionProxyBypassType( + parsed.get(), GURL(), net::BoundNetLog(), &chrome_proxy_info, + event_creator(), &event_was_logged)); } }
diff --git a/components/data_reduction_proxy/core/common/data_reduction_proxy_switches.cc b/components/data_reduction_proxy/core/common/data_reduction_proxy_switches.cc index 2386931..bf676d3 100644 --- a/components/data_reduction_proxy/core/common/data_reduction_proxy_switches.cc +++ b/components/data_reduction_proxy/core/common/data_reduction_proxy_switches.cc
@@ -74,5 +74,8 @@ const char kEnableDataReductionProxyConfigClient[] = "enable-data-reduction-proxy-config-client"; +// The URL from which to retrieve the Data Reduction Proxy configuration. +const char kDataReductionProxyConfigURL[] = "data-reduction-proxy-config-url"; + } // namespace switches } // namespace data_reduction_proxy
diff --git a/components/data_reduction_proxy/core/common/data_reduction_proxy_switches.h b/components/data_reduction_proxy/core/common/data_reduction_proxy_switches.h index 1a8f7c0..90d98df 100644 --- a/components/data_reduction_proxy/core/common/data_reduction_proxy_switches.h +++ b/components/data_reduction_proxy/core/common/data_reduction_proxy_switches.h
@@ -29,6 +29,7 @@ extern const char kEnableDataReductionProxyBypassWarning[]; extern const char kClearDataReductionProxyDataSavings[]; extern const char kEnableDataReductionProxyConfigClient[]; +extern const char kDataReductionProxyConfigURL[]; } // namespace switches } // namespace data_reduction_proxy
diff --git a/components/devtools_discovery.gypi b/components/devtools_discovery.gypi new file mode 100644 index 0000000..9b8232c --- /dev/null +++ b/components/devtools_discovery.gypi
@@ -0,0 +1,27 @@ +# Copyright 2015 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'targets': [ + { + 'target_name': 'devtools_discovery', + 'type': 'static_library', + 'dependencies': [ + '../base/base.gyp:base', + '../content/content.gyp:content_browser', + '../content/content.gyp:content_common', + ], + 'include_dirs': [ + '..', + ], + 'sources': [ + 'devtools_discovery/basic_target_descriptor.cc', + 'devtools_discovery/basic_target_descriptor.h', + 'devtools_discovery/devtools_discovery_manager.cc', + 'devtools_discovery/devtools_discovery_manager.h', + 'devtools_discovery/devtools_target_descriptor.h', + ], + }, + ], +}
diff --git a/components/devtools_discovery/BUILD.gn b/components/devtools_discovery/BUILD.gn new file mode 100644 index 0000000..a733af4 --- /dev/null +++ b/components/devtools_discovery/BUILD.gn
@@ -0,0 +1,19 @@ +# 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("devtools_discovery") { + sources = [ + "basic_target_descriptor.cc", + "basic_target_descriptor.h", + "devtools_discovery_manager.cc", + "devtools_discovery_manager.h", + "devtools_target_descriptor.h", + ] + + deps = [ + "//base", + "//content/public/browser", + "//content/public/common", + ] +}
diff --git a/components/devtools_discovery/DEPS b/components/devtools_discovery/DEPS new file mode 100644 index 0000000..f75ba964 --- /dev/null +++ b/components/devtools_discovery/DEPS
@@ -0,0 +1,5 @@ +include_rules = [ + "+content/public/browser", + "+content/public/common", + "+content/public/test", +]
diff --git a/components/devtools_discovery/OWNERS b/components/devtools_discovery/OWNERS new file mode 100644 index 0000000..fe38b0fa --- /dev/null +++ b/components/devtools_discovery/OWNERS
@@ -0,0 +1,2 @@ +dgozman@chromium.org +pfeldman@chromium.org
diff --git a/components/devtools_discovery/basic_target_descriptor.cc b/components/devtools_discovery/basic_target_descriptor.cc new file mode 100644 index 0000000..bbb62d0 --- /dev/null +++ b/components/devtools_discovery/basic_target_descriptor.cc
@@ -0,0 +1,94 @@ +// 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 "components/devtools_discovery/basic_target_descriptor.h" + +#include "content/public/browser/devtools_agent_host.h" +#include "content/public/browser/favicon_status.h" +#include "content/public/browser/navigation_entry.h" +#include "content/public/browser/web_contents.h" + +using content::DevToolsAgentHost; + +namespace devtools_discovery { + +const char BasicTargetDescriptor::kTypePage[] = "page"; +const char BasicTargetDescriptor::kTypeServiceWorker[] = "service_worker"; +const char BasicTargetDescriptor::kTypeSharedWorker[] = "worker"; +const char BasicTargetDescriptor::kTypeOther[] = "other"; + +BasicTargetDescriptor::BasicTargetDescriptor( + scoped_refptr<DevToolsAgentHost> agent_host) + : agent_host_(agent_host) { + if (content::WebContents* web_contents = agent_host_->GetWebContents()) { + content::NavigationController& controller = web_contents->GetController(); + content::NavigationEntry* entry = controller.GetActiveEntry(); + if (entry != NULL && entry->GetURL().is_valid()) + favicon_url_ = entry->GetFavicon().url; + last_activity_time_ = web_contents->GetLastActiveTime(); + } +} + +BasicTargetDescriptor::~BasicTargetDescriptor() { +} + +std::string BasicTargetDescriptor::GetId() const { + return agent_host_->GetId(); +} + +std::string BasicTargetDescriptor::GetParentId() const { + return std::string(); +} + +std::string BasicTargetDescriptor::GetType() const { + switch (agent_host_->GetType()) { + case DevToolsAgentHost::TYPE_WEB_CONTENTS: + return kTypePage; + case DevToolsAgentHost::TYPE_SERVICE_WORKER: + return kTypeServiceWorker; + case DevToolsAgentHost::TYPE_SHARED_WORKER: + return kTypeSharedWorker; + default: + break; + } + return kTypeOther; +} + +std::string BasicTargetDescriptor::GetTitle() const { + return agent_host_->GetTitle(); +} + +std::string BasicTargetDescriptor::GetDescription() const { + return std::string(); +} + +GURL BasicTargetDescriptor::GetURL() const { + return agent_host_->GetURL(); +} + +GURL BasicTargetDescriptor::GetFaviconURL() const { + return favicon_url_; +} + +base::TimeTicks BasicTargetDescriptor::GetLastActivityTime() const { + return last_activity_time_; +} + +bool BasicTargetDescriptor::IsAttached() const { + return agent_host_->IsAttached(); +} + +scoped_refptr<DevToolsAgentHost> BasicTargetDescriptor::GetAgentHost() const { + return agent_host_; +} + +bool BasicTargetDescriptor::Activate() const { + return agent_host_->Activate(); +} + +bool BasicTargetDescriptor::Close() const { + return agent_host_->Close(); +} + +} // namespace devtools_discovery
diff --git a/components/devtools_discovery/basic_target_descriptor.h b/components/devtools_discovery/basic_target_descriptor.h new file mode 100644 index 0000000..9072e0f5 --- /dev/null +++ b/components/devtools_discovery/basic_target_descriptor.h
@@ -0,0 +1,45 @@ +// 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 COMPONENTS_DEVTOOLS_DISCOVERY_BASIC_TARGET_DESCRIPTOR_H_ +#define COMPONENTS_DEVTOOLS_DISCOVERY_BASIC_TARGET_DESCRIPTOR_H_ + +#include "components/devtools_discovery/devtools_target_descriptor.h" + +namespace devtools_discovery { + +class BasicTargetDescriptor : public DevToolsTargetDescriptor { + public: + explicit BasicTargetDescriptor( + scoped_refptr<content::DevToolsAgentHost> agent_host); + ~BasicTargetDescriptor() override; + + static const char kTypePage[]; + static const char kTypeServiceWorker[]; + static const char kTypeSharedWorker[]; + static const char kTypeOther[]; + + // DevToolsTargetDescriptor implementation. + std::string GetId() const override; + std::string GetParentId() const override; + std::string GetType() const override; + std::string GetTitle() const override; + std::string GetDescription() const override; + GURL GetURL() const override; + GURL GetFaviconURL() const override; + base::TimeTicks GetLastActivityTime() const override; + bool IsAttached() const override; + scoped_refptr<content::DevToolsAgentHost> GetAgentHost() const override; + bool Activate() const override; + bool Close() const override; + + private: + scoped_refptr<content::DevToolsAgentHost> agent_host_; + GURL favicon_url_; + base::TimeTicks last_activity_time_; +}; + +} // namespace devtools_discovery + +#endif // COMPONENTS_DEVTOOLS_DISCOVERY_BASIC_TARGET_DESCRIPTOR_H_
diff --git a/components/devtools_discovery/devtools_discovery_manager.cc b/components/devtools_discovery/devtools_discovery_manager.cc new file mode 100644 index 0000000..3138732d --- /dev/null +++ b/components/devtools_discovery/devtools_discovery_manager.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 "components/devtools_discovery/devtools_discovery_manager.h" + +#include "base/stl_util.h" +#include "components/devtools_discovery/basic_target_descriptor.h" +#include "content/public/browser/devtools_agent_host.h" + +using content::DevToolsAgentHost; + +namespace devtools_discovery { + +// static +DevToolsDiscoveryManager* DevToolsDiscoveryManager::GetInstance() { + return Singleton<DevToolsDiscoveryManager>::get(); +} + +DevToolsDiscoveryManager::DevToolsDiscoveryManager() { +} + +DevToolsDiscoveryManager::~DevToolsDiscoveryManager() { + STLDeleteElements(&providers_); +} + +void DevToolsDiscoveryManager::AddProvider(scoped_ptr<Provider> provider) { + providers_.push_back(provider.release()); +} + +DevToolsTargetDescriptor::List DevToolsDiscoveryManager::GetDescriptors() { + if (providers_.size()) + return GetDescriptorsFromProviders(); + + DevToolsAgentHost::List agent_hosts = DevToolsAgentHost::GetOrCreateAll(); + DevToolsTargetDescriptor::List result; + result.reserve(agent_hosts.size()); + for (const auto& agent_host : agent_hosts) + result.push_back(new BasicTargetDescriptor(agent_host)); + return result; +} + +DevToolsTargetDescriptor::List +DevToolsDiscoveryManager::GetDescriptorsFromProviders() { + DevToolsTargetDescriptor::List result; + for (const auto& provider : providers_) { + DevToolsTargetDescriptor::List partial = provider->GetDescriptors(); + result.insert(result.begin(), partial.begin(), partial.end()); + } + return result; +} + +} // namespace devtools_discovery
diff --git a/components/devtools_discovery/devtools_discovery_manager.h b/components/devtools_discovery/devtools_discovery_manager.h new file mode 100644 index 0000000..c75f844b --- /dev/null +++ b/components/devtools_discovery/devtools_discovery_manager.h
@@ -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. + +#ifndef COMPONENTS_DEVTOOLS_DISCOVERY_DEVTOOLS_DISCOVERY_MANAGER_H_ +#define COMPONENTS_DEVTOOLS_DISCOVERY_DEVTOOLS_DISCOVERY_MANAGER_H_ + +#include <string> + +#include "base/memory/scoped_ptr.h" +#include "base/memory/singleton.h" +#include "components/devtools_discovery/devtools_target_descriptor.h" + +namespace devtools_discovery { + +class DevToolsDiscoveryManager { + public: + class Provider { + public: + virtual ~Provider() {} + virtual DevToolsTargetDescriptor::List GetDescriptors() = 0; + }; + + // Returns single instance of this class. The instance is destroyed on the + // browser main loop exit so this method MUST NOT be called after that point. + static DevToolsDiscoveryManager* GetInstance(); + + void AddProvider(scoped_ptr<Provider> provider); + + DevToolsTargetDescriptor::List GetDescriptors(); + + private: + friend struct DefaultSingletonTraits<DevToolsDiscoveryManager>; + + DevToolsDiscoveryManager(); + ~DevToolsDiscoveryManager(); + DevToolsTargetDescriptor::List GetDescriptorsFromProviders(); + + std::vector<Provider*> providers_; + + DISALLOW_COPY_AND_ASSIGN(DevToolsDiscoveryManager); +}; + +} // namespace devtools_discovery + +#endif // COMPONENTS_DEVTOOLS_DISCOVERY_DEVTOOLS_DISCOVERY_MANAGER_H_
diff --git a/components/devtools_discovery/devtools_target_descriptor.h b/components/devtools_discovery/devtools_target_descriptor.h new file mode 100644 index 0000000..2e5b104 --- /dev/null +++ b/components/devtools_discovery/devtools_target_descriptor.h
@@ -0,0 +1,37 @@ +// 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 COMPONENTS_DEVTOOLS_DISCOVERY_DEVTOOLS_TARGET_DESCRIPTOR_H_ +#define COMPONENTS_DEVTOOLS_DISCOVERY_DEVTOOLS_TARGET_DESCRIPTOR_H_ + +#include <string> +#include <vector> + +#include "base/basictypes.h" +#include "base/memory/ref_counted.h" +#include "base/time/time.h" +#include "content/public/browser/devtools_target.h" +#include "url/gurl.h" + +namespace content { +class DevToolsAgentHost; +} + +namespace devtools_discovery { + +// DevToolsTargetDescriptor provides information about devtools target +// and can be used to manipulate the target and query its details. +// Instantiation and discovery of DevToolsTargetDescriptor instances +// is the responsibility of DevToolsDiscoveryManager. +// TODO(dgozman): remove content::DevToolsTarget once every embedder migrates +// to this descriptor. +class DevToolsTargetDescriptor : public content::DevToolsTarget { + public: + using List = std::vector<DevToolsTargetDescriptor*>; + ~DevToolsTargetDescriptor() override {} +}; + +} // namespace devtools_discovery + +#endif // COMPONENTS_DEVTOOLS_DISCOVERY_DEVTOOLS_TARGET_DESCRIPTOR_H_
diff --git a/components/dom_distiller/android/java/src/org/chromium/components/dom_distiller/content/DistillablePageUtils.java b/components/dom_distiller/android/java/src/org/chromium/components/dom_distiller/content/DistillablePageUtils.java index 03e2a155..39c4ba14 100644 --- a/components/dom_distiller/android/java/src/org/chromium/components/dom_distiller/content/DistillablePageUtils.java +++ b/components/dom_distiller/android/java/src/org/chromium/components/dom_distiller/content/DistillablePageUtils.java
@@ -20,9 +20,9 @@ public void onIsPageDistillableResult(boolean isDistillable); } - public static void isPageDistillable( - WebContents webContents, PageDistillableCallback callback) { - nativeIsPageDistillable(webContents, callback); + public static void isPageDistillable(WebContents webContents, boolean isMobileOptimized, + PageDistillableCallback callback) { + nativeIsPageDistillable(webContents, isMobileOptimized, callback); } @CalledByNative @@ -32,6 +32,6 @@ } private static native void nativeIsPageDistillable( - WebContents webContents, PageDistillableCallback callback); + WebContents webContents, boolean isMobileOptimized, PageDistillableCallback callback); }
diff --git a/components/dom_distiller/content/distillable_page_utils.cc b/components/dom_distiller/content/distillable_page_utils.cc index 1267bfb..5d1519c 100644 --- a/components/dom_distiller/content/distillable_page_utils.cc +++ b/components/dom_distiller/content/distillable_page_utils.cc
@@ -49,6 +49,7 @@ } void IsDistillablePage(content::WebContents* web_contents, + bool is_mobile_optimized, base::Callback<void(bool)> callback) { switch (GetDistillerHeuristicsType()) { case DistillerHeuristicsType::ALWAYS_TRUE: @@ -59,6 +60,12 @@ IsOpenGraphArticle(web_contents, callback); return; case DistillerHeuristicsType::ADABOOST_MODEL: + // The adaboost model is only applied to non-mobile pages. + if (is_mobile_optimized) { + base::MessageLoop::current()->PostTask(FROM_HERE, + base::Bind(callback, false)); + return; + } IsDistillablePageForDetector( web_contents, DistillablePageDetector::GetDefault(), callback); return;
diff --git a/components/dom_distiller/content/distillable_page_utils.h b/components/dom_distiller/content/distillable_page_utils.h index e2887db..a7f8c06 100644 --- a/components/dom_distiller/content/distillable_page_utils.h +++ b/components/dom_distiller/content/distillable_page_utils.h
@@ -15,6 +15,7 @@ // Checks if the page appears to be distillable based on whichever heuristics // are configured to be used (see dom_distiller::GetDistillerHeuristicsType). void IsDistillablePage(content::WebContents* web_contents, + bool is_mobile_optimized, base::Callback<void(bool)> callback); // Checks if the web_contents is has opengraph type=article markup.
diff --git a/components/dom_distiller/content/distillable_page_utils_android.cc b/components/dom_distiller/content/distillable_page_utils_android.cc index 6250398..eac1f247 100644 --- a/components/dom_distiller/content/distillable_page_utils_android.cc +++ b/components/dom_distiller/content/distillable_page_utils_android.cc
@@ -27,6 +27,7 @@ static void IsPageDistillable(JNIEnv* env, jclass jcaller, jobject webContents, + jboolean is_mobile_optimized, jobject callback) { content::WebContents* web_contents( content::WebContents::FromJavaWebContents(webContents)); @@ -40,8 +41,9 @@ base::Passed(&callback_holder), false)); return; } - IsDistillablePage(web_contents, base::Bind(OnIsPageDistillableResult, - base::Passed(&callback_holder))); + IsDistillablePage( + web_contents, is_mobile_optimized, + base::Bind(OnIsPageDistillableResult, base::Passed(&callback_holder))); } bool RegisterDistillablePageUtils(JNIEnv* env) {
diff --git a/components/dom_distiller/content/distillable_page_utils_browsertest.cc b/components/dom_distiller/content/distillable_page_utils_browsertest.cc index 95259828..c320eedb 100644 --- a/components/dom_distiller/content/distillable_page_utils_browsertest.cc +++ b/components/dom_distiller/content/distillable_page_utils_browsertest.cc
@@ -50,7 +50,12 @@ void AddComponentsResources() { base::FilePath pak_file; base::FilePath pak_dir; +#if defined(OS_ANDROID) + CHECK(PathService::Get(base::DIR_ANDROID_APP_DATA, &pak_dir)); + pak_dir = pak_dir.Append(FILE_PATH_LITERAL("paks")); +#else PathService::Get(base::DIR_MODULE, &pak_dir); +#endif // OS_ANDROID pak_file = pak_dir.Append(FILE_PATH_LITERAL("components_tests_resources.pak")); ui::ResourceBundle::GetSharedInstance().AddDataPackFromPath(
diff --git a/components/dom_distiller/content/distiller_page_web_contents_browsertest.cc b/components/dom_distiller/content/distiller_page_web_contents_browsertest.cc index 79d557a..d1d2dff 100644 --- a/components/dom_distiller/content/distiller_page_web_contents_browsertest.cc +++ b/components/dom_distiller/content/distiller_page_web_contents_browsertest.cc
@@ -104,7 +104,12 @@ void AddComponentsResources() { base::FilePath pak_file; base::FilePath pak_dir; +#if defined(OS_ANDROID) + CHECK(PathService::Get(base::DIR_ANDROID_APP_DATA, &pak_dir)); + pak_dir = pak_dir.Append(FILE_PATH_LITERAL("paks")); +#else PathService::Get(base::DIR_MODULE, &pak_dir); +#endif // OS_ANDROID pak_file = pak_dir.Append(FILE_PATH_LITERAL("components_tests_resources.pak")); ui::ResourceBundle::GetSharedInstance().AddDataPackFromPath(
diff --git a/components/dom_distiller/content/test/dom_distiller_js_browsertest.cc b/components/dom_distiller/content/test/dom_distiller_js_browsertest.cc index 53b37b6..6d26962 100644 --- a/components/dom_distiller/content/test/dom_distiller_js_browsertest.cc +++ b/components/dom_distiller/content/test/dom_distiller_js_browsertest.cc
@@ -76,8 +76,14 @@ void AddComponentsResources() { base::FilePath pak_file; base::FilePath pak_dir; +#if defined(OS_ANDROID) + CHECK(PathService::Get(base::DIR_ANDROID_APP_DATA, &pak_dir)); + pak_dir = pak_dir.Append(FILE_PATH_LITERAL("paks")); +#else PathService::Get(base::DIR_MODULE, &pak_dir); - pak_file = pak_dir.Append(FILE_PATH_LITERAL("components_resources.pak")); +#endif // OS_ANDROID + pak_file = + pak_dir.Append(FILE_PATH_LITERAL("components_tests_resources.pak")); ui::ResourceBundle::GetSharedInstance().AddDataPackFromPath( pak_file, ui::SCALE_FACTOR_NONE); }
diff --git a/components/favicon.gypi b/components/favicon.gypi index d150316..d2f9195 100644 --- a/components/favicon.gypi +++ b/components/favicon.gypi
@@ -36,6 +36,8 @@ 'favicon/core/favicon_service.h', 'favicon/core/favicon_url.cc', 'favicon/core/favicon_url.h', + 'favicon/core/large_icon_service.cc', + 'favicon/core/large_icon_service.h', ], 'include_dirs': [ '..',
diff --git a/components/favicon/core/BUILD.gn b/components/favicon/core/BUILD.gn index 7f9b762..f4e8a15e 100644 --- a/components/favicon/core/BUILD.gn +++ b/components/favicon/core/BUILD.gn
@@ -19,6 +19,8 @@ "favicon_service.h", "favicon_url.cc", "favicon_url.h", + "large_icon_service.cc", + "large_icon_service.h", ] deps = [
diff --git a/components/favicon/core/favicon_handler.cc b/components/favicon/core/favicon_handler.cc index aff1626..1d5eac7 100644 --- a/components/favicon/core/favicon_handler.cc +++ b/components/favicon/core/favicon_handler.cc
@@ -235,7 +235,10 @@ url_ = url; favicon_expired_or_incomplete_ = got_favicon_from_history_ = false; + download_requests_.clear(); image_urls_.clear(); + history_results_.clear(); + best_favicon_candidate_ = FaviconCandidate(); // Request the favicon from the history service. In parallel to this the // renderer is going to notify us (well WebContents) when the favicon url is @@ -298,19 +301,12 @@ if (ShouldSaveFavicon(url)) SetHistoryFavicons(url, icon_url, icon_type, image); - if (!UrlMatches(url, url_) || PageChangedSinceFaviconWasRequested()) - return; - - NotifyFaviconAvailable( - icon_url, - image, - icon_type == favicon_base::FAVICON && !download_largest_icon_); + NotifyFaviconAvailable(icon_url, image); } void FaviconHandler::NotifyFaviconAvailable( const std::vector<favicon_base::FaviconRawBitmapResult>& - favicon_bitmap_results, - bool is_active_favicon) { + favicon_bitmap_results) { gfx::Image resized_image = favicon_base::SelectFaviconFramesFromPNGs( favicon_bitmap_results, favicon_base::GetFaviconScales(), @@ -319,23 +315,27 @@ // not matter which result we get the |icon_url| from. const GURL icon_url = favicon_bitmap_results.empty() ? GURL() : favicon_bitmap_results[0].icon_url; - NotifyFaviconAvailable(icon_url, resized_image, is_active_favicon); + NotifyFaviconAvailable(icon_url, resized_image); } void FaviconHandler::NotifyFaviconAvailable(const GURL& icon_url, - const gfx::Image& image, - bool is_active_favicon) { + const gfx::Image& image) { gfx::Image image_with_adjusted_colorspace = image; favicon_base::SetFaviconColorSpace(&image_with_adjusted_colorspace); + bool is_active_favicon = + (handler_type_ == FAVICON && !download_largest_icon_); + driver_->OnFaviconAvailable( image_with_adjusted_colorspace, icon_url, is_active_favicon); } void FaviconHandler::OnUpdateFaviconURL( const std::vector<FaviconURL>& candidates) { + download_requests_.clear(); image_urls_.clear(); best_favicon_candidate_ = FaviconCandidate(); + for (const FaviconURL& candidate : candidates) { if (!candidate.icon_url.is_empty() && (candidate.icon_type & icon_types_)) image_urls_.push_back(candidate); @@ -393,14 +393,19 @@ DownloadRequest download_request = i->second; download_requests_.erase(i); - if (current_candidate() && - DoUrlAndIconMatch(*current_candidate(), - image_url, - download_request.icon_type)) { - bool request_next_icon = true; + if (PageChangedSinceFaviconWasRequested() || + !current_candidate() || + !DoUrlAndIconMatch(*current_candidate(), + image_url, + download_request.icon_type)) { + return; + } + + bool request_next_icon = true; + if (!bitmaps.empty()) { float score = 0.0f; gfx::ImageSkia image_skia; - if (download_largest_icon_ && !bitmaps.empty()) { + if (download_largest_icon_) { int index = -1; // Use the largest bitmap if FaviconURL doesn't have sizes attribute. if (current_candidate()->icon_sizes.empty()) { @@ -424,29 +429,26 @@ gfx::Image image(image_skia); // The downloaded icon is still valid when there is no FaviconURL update // during the downloading. - if (!bitmaps.empty()) { - request_next_icon = !UpdateFaviconCandidate( - download_request.url, image_url, image, score, - download_request.icon_type); - } + request_next_icon = !UpdateFaviconCandidate( + download_request.url, image_url, image, score, + download_request.icon_type); } - if (request_next_icon && !PageChangedSinceFaviconWasRequested() && - image_urls_.size() > 1) { - // Remove the first member of image_urls_ and process the remaining. - image_urls_.erase(image_urls_.begin()); - ProcessCurrentUrl(); - } else if (best_favicon_candidate_.icon_type != - favicon_base::INVALID_ICON) { - // No more icons to request, set the favicon from the candidate. - SetFavicon(best_favicon_candidate_.url, - best_favicon_candidate_.image_url, - best_favicon_candidate_.image, - best_favicon_candidate_.icon_type); - // Reset candidate. - image_urls_.clear(); - download_requests_.clear(); - best_favicon_candidate_ = FaviconCandidate(); - } + } + + if (request_next_icon && image_urls_.size() > 1) { + // Remove the first member of image_urls_ and process the remaining. + image_urls_.erase(image_urls_.begin()); + ProcessCurrentUrl(); + } else if (best_favicon_candidate_.icon_type != favicon_base::INVALID_ICON) { + // No more icons to request, set the favicon from the candidate. + SetFavicon(best_favicon_candidate_.url, + best_favicon_candidate_.image_url, + best_favicon_candidate_.image, + best_favicon_candidate_.icon_type); + // Reset candidate. + image_urls_.clear(); + download_requests_.clear(); + best_favicon_candidate_ = FaviconCandidate(); } } @@ -569,7 +571,7 @@ // doesn't have an icon. Set the favicon now, and if the favicon turns out // to be expired (or the wrong url) we'll fetch later on. This way the // user doesn't see a flash of the default favicon. - NotifyFaviconAvailable(favicon_bitmap_results, true); + NotifyFaviconAvailable(favicon_bitmap_results); } else { // If |favicon_bitmap_results| does not have any valid results, treat the // favicon as if it's expired. @@ -599,7 +601,7 @@ // renderer to download the icon. if (has_valid_result && (handler_type_ != FAVICON || download_largest_icon_)) - NotifyFaviconAvailable(favicon_bitmap_results, false); + NotifyFaviconAvailable(favicon_bitmap_results); } void FaviconHandler::DownloadFaviconOrAskFaviconService( @@ -647,7 +649,7 @@ // There is a favicon, set it now. If expired we'll download the current // one again, but at least the user will get some icon instead of the // default and most likely the current one is fine anyway. - NotifyFaviconAvailable(favicon_bitmap_results, true); + NotifyFaviconAvailable(favicon_bitmap_results); } if (has_expired_or_incomplete_result) { // The favicon is out of date. Request the current one. @@ -668,11 +670,11 @@ if (has_valid_result && (handler_type_ != FAVICON || download_largest_icon_)) { - NotifyFaviconAvailable(favicon_bitmap_results, false); + NotifyFaviconAvailable(favicon_bitmap_results); } } -int FaviconHandler::ScheduleDownload(const GURL& url, +void FaviconHandler::ScheduleDownload(const GURL& url, const GURL& image_url, favicon_base::IconType icon_type) { // A max bitmap size is specified to avoid receiving huge bitmaps in @@ -686,8 +688,6 @@ download_requests_[download_id] = DownloadRequest(url, image_url, icon_type); } - - return download_id; } void FaviconHandler::SortAndPruneImageUrls() {
diff --git a/components/favicon/core/favicon_handler.h b/components/favicon/core/favicon_handler.h index 8b7ab97..3fee8be 100644 --- a/components/favicon/core/favicon_handler.h +++ b/components/favicon/core/favicon_handler.h
@@ -212,9 +212,9 @@ // Schedules a download for the specified entry. This adds the request to // download_requests_. - int ScheduleDownload(const GURL& url, - const GURL& image_url, - favicon_base::IconType icon_type); + void ScheduleDownload(const GURL& url, + const GURL& image_url, + favicon_base::IconType icon_type); // Updates |favicon_candidate_| and returns true if it is an exact match. bool UpdateFaviconCandidate(const GURL& url, @@ -233,11 +233,9 @@ // FaviconDriver::NotifyFaviconAvailable() for |is_active_favicon| in detail. void NotifyFaviconAvailable( const std::vector<favicon_base::FaviconRawBitmapResult>& - favicon_bitmap_results, - bool is_active_favicon); + favicon_bitmap_results); void NotifyFaviconAvailable(const GURL& icon_url, - const gfx::Image& image, - bool is_active_favicon); + const gfx::Image& image); // Return the current candidate if any. favicon::FaviconURL* current_candidate() { @@ -266,9 +264,6 @@ // URL of the page we're requesting the favicon for. GURL url_; - // Whether we are waiting for data from the FaviconService. - bool waiting_for_favicon_service_data_; - // Whether we got data back for the initial request to the FaviconService. bool got_favicon_from_history_;
diff --git a/components/favicon/core/large_icon_service.cc b/components/favicon/core/large_icon_service.cc new file mode 100644 index 0000000..702761c8 --- /dev/null +++ b/components/favicon/core/large_icon_service.cc
@@ -0,0 +1,77 @@ +// 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 "components/favicon/core/large_icon_service.h" + +#include "components/favicon/core/favicon_service.h" +#include "components/favicon_base/fallback_icon_style.h" +#include "components/favicon_base/favicon_types.h" + +namespace favicon { + +LargeIconService::LargeIconService(FaviconService* favicon_service) + : favicon_service_(favicon_service) { + large_icon_types_.push_back(favicon_base::IconType::FAVICON); + large_icon_types_.push_back(favicon_base::IconType::TOUCH_ICON); + large_icon_types_.push_back(favicon_base::IconType::TOUCH_PRECOMPOSED_ICON); +} + +LargeIconService::~LargeIconService() { +} + +base::CancelableTaskTracker::TaskId + LargeIconService::GetLargeIconOrFallbackStyle( + const GURL& page_url, + int desired_size_in_pixel, + const favicon_base::LargeIconCallback& callback, + base::CancelableTaskTracker* tracker) { + // TODO(beaudoin): For now this is just a wrapper around + // GetLargestRawFaviconForPageURL. Add the logic required to select the best + // possible large icon. Also add logic to fetch-on-demand when the URL of + // a large icon is known but its bitmap is not available. + return favicon_service_->GetLargestRawFaviconForPageURL( + page_url, + large_icon_types_, + desired_size_in_pixel, + base::Bind(&LargeIconService::RunLargeIconCallback, + base::Unretained(this), callback, desired_size_in_pixel), + tracker); +} + +void LargeIconService::RunLargeIconCallback( + const favicon_base::LargeIconCallback& callback, + int desired_size_in_pixel, + const favicon_base::FaviconRawBitmapResult& bitmap_result) { + // If there are no bitmaps, return a result with an empty |bitmap| and a + // default |fallback_icon_style|. + favicon_base::LargeIconResult result; + if (!bitmap_result.is_valid()) { + callback.Run(result); + return; + } + + // If there is a bitmap but it's smaller than the requested size or + // non-square, compute its dominant color and use it as background in + // |fallback_icon_style|. + if (bitmap_result.pixel_size.width() < desired_size_in_pixel || + bitmap_result.pixel_size.height() < desired_size_in_pixel || + bitmap_result.pixel_size.width() != bitmap_result.pixel_size.height()) { + // TODO(beaudoin): Resize the icon if it's large enough. Alternatively, + // return it and let the HTML resize it. + result.fallback_icon_style.reset(new favicon_base::FallbackIconStyle()); + favicon_base::SetDominantColorAsBackground( + bitmap_result.bitmap_data, result.fallback_icon_style.get()); + callback.Run(result); + return; + } + + // The bitmap is square and at least as large as the requested one, return + // it. + // TODO(beaudoin): Resize the icon if it's too large. Alternatively, return + // it and let the HTML resize it. + result.bitmap = bitmap_result; + callback.Run(result); +} + +} // namespace favicon
diff --git a/components/favicon/core/large_icon_service.h b/components/favicon/core/large_icon_service.h new file mode 100644 index 0000000..1063fd7 --- /dev/null +++ b/components/favicon/core/large_icon_service.h
@@ -0,0 +1,65 @@ +// 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 COMPONENTS_FAVICON_CORE_LARGE_ICON_SERVICE_H_ +#define COMPONENTS_FAVICON_CORE_LARGE_ICON_SERVICE_H_ + +#include <vector> + +#include "base/task/cancelable_task_tracker.h" +#include "components/favicon_base/favicon_callback.h" +#include "components/keyed_service/core/keyed_service.h" + +class GURL; + +namespace favicon_base { +struct FaviconRawBitmapResult; +} + +namespace favicon { + +class FaviconService; + +// The large icon service provides methods to access large icons. It relies on +// the favicon service. +class LargeIconService : public KeyedService { + public: + explicit LargeIconService(FaviconService* favicon_service); + + ~LargeIconService() override; + + // Requests the best large icon for the page at |page_url| given the requested + // |desired_size_in_pixel|. If no good large icon can be found, returns the + // fallback style to use, for which the background is set to the dominant + // color of a smaller icon when one is available. This function returns the + // style of the fallback icon rather than the rendered version so that clients + // can render the icon themselves. + base::CancelableTaskTracker::TaskId GetLargeIconOrFallbackStyle( + const GURL& page_url, + int desired_size_in_pixel, + const favicon_base::LargeIconCallback& callback, + base::CancelableTaskTracker* tracker); + + private: + // Intermediate callback for GetLargeIconOrFallbackStyle(). Ensures the large + // icon is at least the desired size, if not compute the icon fallback style + // and use it to invoke |callback|. + void RunLargeIconCallback( + const favicon_base::LargeIconCallback& callback, + int desired_size_in_pixel, + const favicon_base::FaviconRawBitmapResult& bitmap_result); + + FaviconService* favicon_service_; + + // A pre-populated list of the types of icon files to consider when looking + // for large icons. This is an optimization over populating an icon type + // vector on each request. + std::vector<int> large_icon_types_; + + DISALLOW_COPY_AND_ASSIGN(LargeIconService); +}; + +} // namespace favicon + +#endif // COMPONENTS_FAVICON_CORE_LARGE_ICON_SERVICE_H_
diff --git a/components/favicon_base/fallback_icon_style.cc b/components/favicon_base/fallback_icon_style.cc index e620613..d3e395b1 100644 --- a/components/favicon_base/fallback_icon_style.cc +++ b/components/favicon_base/fallback_icon_style.cc
@@ -4,6 +4,9 @@ #include "components/favicon_base/fallback_icon_style.h" +#include <algorithm> + +#include "ui/gfx/color_analysis.h" #include "ui/gfx/color_utils.h" namespace favicon_base { @@ -12,14 +15,19 @@ // Luminance threshold for background color determine whether to use dark or // light text color. -int kDarkTextLuminanceThreshold = 190; +const int kDarkTextLuminanceThreshold = 190; + +// The maximum luminance of the background color to ensure light text is +// readable. +const double kMaxBackgroundColorLuminance = 0.67; // Default values for FallbackIconStyle. -SkColor kDefaultBackgroundColor = SkColorSetRGB(0x80, 0x80, 0x80); -SkColor kDefaultTextColorDark = SK_ColorBLACK; -SkColor kDefaultTextColorLight = SK_ColorWHITE; -double kDefaultFontSizeRatio = 0.8; -double kDefaultRoundness = 0.125; // 1 / 8. +const SkColor kDefaultBackgroundColor = SkColorSetRGB(0x78, 0x78, 0x78); +const SkColor kDefaultTextColorDark = SK_ColorBLACK; +const SkColor kDefaultTextColorLight = SK_ColorWHITE; +const double kDefaultFontSizeRatio = 0.44; +const double kDefaultRoundness = 0; // Square. Round corners are applied + // externally (Javascript or Java). } // namespace @@ -45,4 +53,18 @@ style.roundness >= 0.0 && style.roundness <= 1.0; } +void SetDominantColorAsBackground( + const scoped_refptr<base::RefCountedMemory>& bitmap_data, + FallbackIconStyle* style) { + SkColor dominant_color = + color_utils::CalculateKMeanColorOfPNG(bitmap_data); + // Assumes |style.text_color| is light, and clamps luminance down to a + // reasonable maximum value so text is readable. + color_utils::HSL color_hsl; + color_utils::SkColorToHSL(dominant_color, &color_hsl); + color_hsl.l = std::min(color_hsl.l, kMaxBackgroundColorLuminance); + style->background_color = + color_utils::HSLToSkColor(color_hsl, SK_AlphaOPAQUE); +} + } // namespace favicon_base
diff --git a/components/favicon_base/fallback_icon_style.h b/components/favicon_base/fallback_icon_style.h index 097636b0..84842ed 100644 --- a/components/favicon_base/fallback_icon_style.h +++ b/components/favicon_base/fallback_icon_style.h
@@ -5,6 +5,7 @@ #ifndef COMPONENTS_FAVICON_BASE_FALLBACK_ICON_STYLE_H_ #define COMPONENTS_FAVICON_BASE_FALLBACK_ICON_STYLE_H_ +#include "base/memory/ref_counted_memory.h" #include "third_party/skia/include/core/SkColor.h" namespace favicon_base { @@ -37,6 +38,13 @@ // Returns whether |style| values are within bounds. bool ValidateFallbackIconStyle(const FallbackIconStyle& style); +// Set |style|'s background color to the dominant color of |bitmap_data|, +// clamping luminance down to a reasonable maximum value so that light text is +// readable. +void SetDominantColorAsBackground( + const scoped_refptr<base::RefCountedMemory>& bitmap_data, + FallbackIconStyle* style); + } // namespace favicon_base #endif // COMPONENTS_FAVICON_BASE_FALLBACK_ICON_STYLE_H_
diff --git a/components/favicon_base/favicon_callback.h b/components/favicon_base/favicon_callback.h index 7616a11..96029f1c 100644 --- a/components/favicon_base/favicon_callback.h +++ b/components/favicon_base/favicon_callback.h
@@ -13,6 +13,7 @@ struct FaviconRawBitmapResult; struct FaviconImageResult; +struct LargeIconResult; // Callback for functions that can be used to return a |gfx::Image| and the // |GURL| it is loaded from. They are returned as a |FaviconImageResult| object. @@ -29,6 +30,11 @@ typedef base::Callback<void(const std::vector<FaviconRawBitmapResult>&)> FaviconResultsCallback; +// Callback for functions returning data for a large icon. |LargeIconResult| +// will contain either the raw bitmap for a large icon or the style of the +// fallback to use if a sufficiently large icon could not be found. +typedef base::Callback<void(const LargeIconResult&)> LargeIconCallback; + } // namespace favicon_base #endif // COMPONENTS_FAVICON_BASE_FAVICON_CALLBACK_H_
diff --git a/components/favicon_base/favicon_types.cc b/components/favicon_base/favicon_types.cc index 0188f1e..e69f1d7be 100644 --- a/components/favicon_base/favicon_types.cc +++ b/components/favicon_base/favicon_types.cc
@@ -4,6 +4,8 @@ #include "components/favicon_base/favicon_types.h" +#include "components/favicon_base/fallback_icon_style.h" + namespace favicon_base { // --------------------------------------------------------- @@ -20,7 +22,13 @@ : expired(false), icon_type(INVALID_ICON) { } -FaviconRawBitmapResult::~FaviconRawBitmapResult() { -} +FaviconRawBitmapResult::~FaviconRawBitmapResult() {} + +// -------------------------------------------------------- +// LargeIconResult + +LargeIconResult::LargeIconResult() {} + +LargeIconResult::~LargeIconResult() {} } // namespace favicon_base
diff --git a/components/favicon_base/favicon_types.h b/components/favicon_base/favicon_types.h index ea64754..f7c812ab 100644 --- a/components/favicon_base/favicon_types.h +++ b/components/favicon_base/favicon_types.h
@@ -6,12 +6,15 @@ #define COMPONENTS_FAVICON_BASE_FAVICON_TYPES_H_ #include "base/memory/ref_counted_memory.h" +#include "base/memory/scoped_ptr.h" #include "ui/gfx/geometry/size.h" #include "ui/gfx/image/image.h" #include "url/gurl.h" namespace favicon_base { +struct FallbackIconStyle; + typedef int64 FaviconID; // Defines the icon types. They are also stored in icon_type field of favicons @@ -72,6 +75,22 @@ // HistoryBackend::SetFavicons(). typedef FaviconRawBitmapResult FaviconRawBitmapData; +// Result returned by LargeIconService::GetLargeIconOrFallbackStyle(). Contains +// either the bitmap data if the favicon database has a sufficiently large +// favicon bitmap and the style of the fallback icon otherwise. +struct LargeIconResult { + LargeIconResult(); + ~LargeIconResult(); + + // The bitmap from the favicon database if the database has a sufficiently + // large one. + FaviconRawBitmapResult bitmap; + + // The fallback icon style if a sufficiently large icon isn't available. This + // uses the dominant color of a smaller icon as the background if available. + scoped_ptr<FallbackIconStyle> fallback_icon_style; +}; + } // namespace favicon_base #endif // COMPONENTS_FAVICON_BASE_FAVICON_TYPES_H_
diff --git a/mojo/services/html_viewer/BUILD.gn b/components/html_viewer/BUILD.gn similarity index 84% rename from mojo/services/html_viewer/BUILD.gn rename to components/html_viewer/BUILD.gn index b36bd416..75856bcb 100644 --- a/mojo/services/html_viewer/BUILD.gn +++ b/components/html_viewer/BUILD.gn
@@ -21,7 +21,7 @@ } action("generate_blink_resource_map") { - script = "//mojo/services/html_viewer/generate_blink_resource_map.py" + script = "//components/html_viewer/generate_blink_resource_map.py" args = [ "--pak-file", rebase_path("$target_gen_dir/unified_blink_resources.pak"), @@ -62,32 +62,37 @@ "discardable_memory_allocator.h", "html_document.cc", "html_document.h", + "mock_web_blob_registry_impl.cc", + "mock_web_blob_registry_impl.h", "touch_handler.cc", "touch_handler.h", - "webclipboard_impl.cc", - "webclipboard_impl.h", - "webcookiejar_impl.cc", - "webcookiejar_impl.h", - "weblayertreeview_impl.cc", - "weblayertreeview_impl.h", - "webmediaplayer_factory.cc", - "webmediaplayer_factory.h", - "webmimeregistry_impl.cc", - "webmimeregistry_impl.h", - "webnotificationmanager_impl.cc", - "webnotificationmanager_impl.h", - "webscheduler_impl.cc", - "webscheduler_impl.h", - "websockethandle_impl.cc", - "websockethandle_impl.h", - "webstoragenamespace_impl.cc", - "webstoragenamespace_impl.h", - "webthemeengine_impl.cc", - "webthemeengine_impl.h", - "webthread_impl.cc", - "webthread_impl.h", - "weburlloader_impl.cc", - "weburlloader_impl.h", + "web_clipboard_impl.cc", + "web_clipboard_impl.h", + "web_cookie_jar_impl.cc", + "web_cookie_jar_impl.h", + "web_layer_tree_view_impl.cc", + "web_layer_tree_view_impl.h", + "web_media_player_factory.cc", + "web_media_player_factory.h", + "web_media_player_factory.h", + "web_message_port_channel_impl.cc", + "web_message_port_channel_impl.h", + "web_mime_registry_impl.cc", + "web_mime_registry_impl.h", + "web_notification_manager_impl.cc", + "web_notification_manager_impl.h", + "web_scheduler_impl.cc", + "web_scheduler_impl.h", + "web_socket_handle_impl.cc", + "web_socket_handle_impl.h", + "web_storage_namespace_impl.cc", + "web_storage_namespace_impl.h", + "web_theme_engine_impl.cc", + "web_theme_engine_impl.h", + "web_thread_impl.cc", + "web_thread_impl.h", + "web_url_loader_impl.cc", + "web_url_loader_impl.h", ] include_dirs = [ "third_party/WebKit" ]
diff --git a/mojo/services/html_viewer/DEPS b/components/html_viewer/DEPS similarity index 85% rename from mojo/services/html_viewer/DEPS rename to components/html_viewer/DEPS index 867c136b..c513c40c 100644 --- a/mojo/services/html_viewer/DEPS +++ b/components/html_viewer/DEPS
@@ -6,13 +6,16 @@ "+components/webcrypto", "+gin", "+media", - "+mojo/cc", - "+mojo/converters/surfaces", "+mojo/application", + "+mojo/cc", + "+mojo/common", + "+mojo/converters/surfaces", + "+mojo/public", "+mojo/services/network", "+net/base", "+net/test/spawned_test_server", "+skia", + "+third_party/mojo/src/mojo/public", "+third_party/mojo_services/src", "+third_party/WebKit/public", "+third_party/skia/include",
diff --git a/mojo/services/html_viewer/OWNERS b/components/html_viewer/OWNERS similarity index 100% rename from mojo/services/html_viewer/OWNERS rename to components/html_viewer/OWNERS
diff --git a/mojo/services/html_viewer/android/android_hooks.cc b/components/html_viewer/android/android_hooks.cc similarity index 89% rename from mojo/services/html_viewer/android/android_hooks.cc rename to components/html_viewer/android/android_hooks.cc index a1a29d2..ab553fe9 100644 --- a/mojo/services/html_viewer/android/android_hooks.cc +++ b/components/html_viewer/android/android_hooks.cc
@@ -8,7 +8,7 @@ #include "base/android/jni_android.h" #include "base/android/library_loader/library_loader_hooks.h" #include "base/bind.h" -#include "mojo/services/html_viewer/jni/Main_jni.h" +#include "components/html_viewer/jni/Main_jni.h" namespace { bool RegisterJNI(JNIEnv* env) { @@ -16,7 +16,8 @@ } bool Init() { - Java_Main_init(base::android::AttachCurrentThread()); + Java_Main_init(base::android::AttachCurrentThread(), + base::android::GetApplicationContext()); return true; } } // namespace
diff --git a/mojo/services/html_viewer/android/java/org/chromium/html_viewer/Main.java b/components/html_viewer/android/java/org/chromium/html_viewer/Main.java similarity index 84% rename from mojo/services/html_viewer/android/java/org/chromium/html_viewer/Main.java rename to components/html_viewer/android/java/org/chromium/html_viewer/Main.java index 283029d1..87a7b59 100644 --- a/mojo/services/html_viewer/android/java/org/chromium/html_viewer/Main.java +++ b/components/html_viewer/android/java/org/chromium/html_viewer/Main.java
@@ -4,6 +4,8 @@ package org.chromium.html_viewer; +import android.content.Context; + import org.chromium.base.CalledByNative; import org.chromium.base.PathUtils; @@ -17,7 +19,7 @@ @SuppressWarnings("unused") @CalledByNative - private static void init() { - PathUtils.setPrivateDataDirectorySuffix(PRIVATE_DATA_DIRECTORY_SUFFIX); + private static void init(Context context) { + PathUtils.setPrivateDataDirectorySuffix(PRIVATE_DATA_DIRECTORY_SUFFIX, context); } }
diff --git a/mojo/services/html_viewer/ax_provider_apptest.cc b/components/html_viewer/ax_provider_apptest.cc similarity index 100% rename from mojo/services/html_viewer/ax_provider_apptest.cc rename to components/html_viewer/ax_provider_apptest.cc
diff --git a/mojo/services/html_viewer/ax_provider_impl.cc b/components/html_viewer/ax_provider_impl.cc similarity index 95% rename from mojo/services/html_viewer/ax_provider_impl.cc rename to components/html_viewer/ax_provider_impl.cc index 11a1356b..bc7dc3cc 100644 --- a/mojo/services/html_viewer/ax_provider_impl.cc +++ b/components/html_viewer/ax_provider_impl.cc
@@ -2,9 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "mojo/services/html_viewer/ax_provider_impl.h" +#include "components/html_viewer/ax_provider_impl.h" -#include "mojo/services/html_viewer/blink_basic_type_converters.h" +#include "components/html_viewer/blink_basic_type_converters.h" #include "third_party/WebKit/public/platform/WebURL.h" #include "third_party/WebKit/public/web/WebAXObject.h" #include "third_party/WebKit/public/web/WebSettings.h"
diff --git a/mojo/services/html_viewer/ax_provider_impl.h b/components/html_viewer/ax_provider_impl.h similarity index 87% rename from mojo/services/html_viewer/ax_provider_impl.h rename to components/html_viewer/ax_provider_impl.h index ec6c8de..cea1d45c 100644 --- a/mojo/services/html_viewer/ax_provider_impl.h +++ b/components/html_viewer/ax_provider_impl.h
@@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef MOJO_SERVICES_HTML_VIEWER_AX_PROVIDER_IMPL_H_ -#define MOJO_SERVICES_HTML_VIEWER_AX_PROVIDER_IMPL_H_ +#ifndef COMPONENTS_HTML_VIEWER_AX_PROVIDER_IMPL_H_ +#define COMPONENTS_HTML_VIEWER_AX_PROVIDER_IMPL_H_ #include "third_party/mojo/src/mojo/public/cpp/bindings/interface_impl.h" #include "third_party/mojo_services/src/accessibility/public/interfaces/accessibility.mojom.h" @@ -36,4 +36,4 @@ } // namespace html_viewer -#endif // MOJO_SERVICES_HTML_VIEWER_AX_PROVIDER_IMPL_H_ +#endif // COMPONENTS_HTML_VIEWER_AX_PROVIDER_IMPL_H_
diff --git a/mojo/services/html_viewer/ax_provider_impl_unittest.cc b/components/html_viewer/ax_provider_impl_unittest.cc similarity index 97% rename from mojo/services/html_viewer/ax_provider_impl_unittest.cc rename to components/html_viewer/ax_provider_impl_unittest.cc index ec627cf..f8688d3 100644 --- a/mojo/services/html_viewer/ax_provider_impl_unittest.cc +++ b/components/html_viewer/ax_provider_impl_unittest.cc
@@ -2,12 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "mojo/services/html_viewer/ax_provider_impl.h" +#include "components/html_viewer/ax_provider_impl.h" #include "base/bind.h" #include "base/message_loop/message_loop.h" +#include "components/html_viewer/blink_platform_impl.h" #include "gin/v8_initializer.h" -#include "mojo/services/html_viewer/blink_platform_impl.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/WebKit/public/platform/WebData.h" #include "third_party/WebKit/public/platform/WebURL.h"
diff --git a/mojo/services/html_viewer/blink_basic_type_converters.cc b/components/html_viewer/blink_basic_type_converters.cc similarity index 94% rename from mojo/services/html_viewer/blink_basic_type_converters.cc rename to components/html_viewer/blink_basic_type_converters.cc index 5324a7c3..f7a5ff47 100644 --- a/mojo/services/html_viewer/blink_basic_type_converters.cc +++ b/components/html_viewer/blink_basic_type_converters.cc
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "mojo/services/html_viewer/blink_basic_type_converters.h" +#include "components/html_viewer/blink_basic_type_converters.h" #include "third_party/WebKit/public/platform/WebRect.h" #include "third_party/WebKit/public/platform/WebString.h"
diff --git a/mojo/services/html_viewer/blink_basic_type_converters.h b/components/html_viewer/blink_basic_type_converters.h similarity index 87% rename from mojo/services/html_viewer/blink_basic_type_converters.h rename to components/html_viewer/blink_basic_type_converters.h index eb762a85..ad2d08b 100644 --- a/mojo/services/html_viewer/blink_basic_type_converters.h +++ b/components/html_viewer/blink_basic_type_converters.h
@@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef MOJO_SERVICES_HTML_VIEWER_BLINK_BASIC_TYPE_CONVERTERS_H_ -#define MOJO_SERVICES_HTML_VIEWER_BLINK_BASIC_TYPE_CONVERTERS_H_ +#ifndef COMPONENTS_HTML_VIEWER_BLINK_BASIC_TYPE_CONVERTERS_H_ +#define COMPONENTS_HTML_VIEWER_BLINK_BASIC_TYPE_CONVERTERS_H_ #include "third_party/mojo/src/mojo/public/cpp/bindings/type_converter.h" @@ -49,4 +49,4 @@ } // namespace mojo -#endif // MOJO_SERVICES_HTML_VIEWER_BLINK_BASIC_TYPE_CONVERTERS_H_ +#endif // COMPONENTS_HTML_VIEWER_BLINK_BASIC_TYPE_CONVERTERS_H_
diff --git a/mojo/services/html_viewer/blink_input_events_type_converters.cc b/components/html_viewer/blink_input_events_type_converters.cc similarity index 98% rename from mojo/services/html_viewer/blink_input_events_type_converters.cc rename to components/html_viewer/blink_input_events_type_converters.cc index 3599df6..0fd8366 100644 --- a/mojo/services/html_viewer/blink_input_events_type_converters.cc +++ b/components/html_viewer/blink_input_events_type_converters.cc
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "mojo/services/html_viewer/blink_input_events_type_converters.h" +#include "components/html_viewer/blink_input_events_type_converters.h" #include "base/logging.h" #include "base/time/time.h"
diff --git a/mojo/services/html_viewer/blink_input_events_type_converters.h b/components/html_viewer/blink_input_events_type_converters.h similarity index 71% rename from mojo/services/html_viewer/blink_input_events_type_converters.h rename to components/html_viewer/blink_input_events_type_converters.h index 442239c..6ea239c 100644 --- a/mojo/services/html_viewer/blink_input_events_type_converters.h +++ b/components/html_viewer/blink_input_events_type_converters.h
@@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef MOJO_SERVICES_HTML_VIEWER_BLINK_INPUT_EVENTS_TYPE_CONVERTERS_H_ -#define MOJO_SERVICES_HTML_VIEWER_BLINK_INPUT_EVENTS_TYPE_CONVERTERS_H_ +#ifndef COMPONENTS_HTML_VIEWER_BLINK_INPUT_EVENTS_TYPE_CONVERTERS_H_ +#define COMPONENTS_HTML_VIEWER_BLINK_INPUT_EVENTS_TYPE_CONVERTERS_H_ #include "base/memory/scoped_ptr.h" #include "third_party/mojo_services/src/input_events/public/interfaces/input_events.mojom.h" @@ -21,4 +21,4 @@ } // namespace mojo -#endif // MOJO_SERVICES_HTML_VIEWER_BLINK_INPUT_EVENTS_TYPE_CONVERTERS_H_ +#endif // COMPONENTS_HTML_VIEWER_BLINK_INPUT_EVENTS_TYPE_CONVERTERS_H_
diff --git a/mojo/services/html_viewer/blink_platform_impl.cc b/components/html_viewer/blink_platform_impl.cc similarity index 91% rename from mojo/services/html_viewer/blink_platform_impl.cc rename to components/html_viewer/blink_platform_impl.cc index 629ffb0..4c4ff85 100644 --- a/mojo/services/html_viewer/blink_platform_impl.cc +++ b/components/html_viewer/blink_platform_impl.cc
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "mojo/services/html_viewer/blink_platform_impl.h" +#include "components/html_viewer/blink_platform_impl.h" #include <cmath> @@ -12,12 +12,13 @@ #include "base/synchronization/waitable_event.h" #include "base/threading/platform_thread.h" #include "base/time/time.h" -#include "mojo/services/html_viewer/blink_resource_constants.h" -#include "mojo/services/html_viewer/webclipboard_impl.h" -#include "mojo/services/html_viewer/webcookiejar_impl.h" -#include "mojo/services/html_viewer/websockethandle_impl.h" -#include "mojo/services/html_viewer/webthread_impl.h" -#include "mojo/services/html_viewer/weburlloader_impl.h" +#include "components/html_viewer/blink_resource_constants.h" +#include "components/html_viewer/web_clipboard_impl.h" +#include "components/html_viewer/web_cookie_jar_impl.h" +#include "components/html_viewer/web_message_port_channel_impl.h" +#include "components/html_viewer/web_socket_handle_impl.h" +#include "components/html_viewer/web_thread_impl.h" +#include "components/html_viewer/web_url_loader_impl.h" #include "net/base/data_url.h" #include "net/base/mime_util.h" #include "net/base/net_errors.h" @@ -35,7 +36,7 @@ // TODO(darin): Figure out what our UA should really be. const char kDefaultUserAgentString[] = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) " - "Chrome/35.0.1916.153 Safari/537.36"; + "Chrome/42.0.2311.68 Safari/537.36"; class WebWaitableEventImpl : public blink::WebWaitableEvent { public: @@ -104,6 +105,10 @@ return blink::WebString::fromUTF8("en-US"); } +blink::WebBlobRegistry* BlinkPlatformImpl::blobRegistry() { + return &blob_registry_; +} + double BlinkPlatformImpl::currentTime() { return base::Time::Now().ToDoubleT(); } @@ -169,6 +174,12 @@ return &compositor_support_; } +void BlinkPlatformImpl::createMessageChannel( + blink::WebMessagePortChannel** channel1, + blink::WebMessagePortChannel** channel2) { + WebMessagePortChannelImpl::CreatePair(channel1, channel2); +} + blink::WebScrollbarBehavior* BlinkPlatformImpl::scrollbarBehavior() { return &scrollbar_behavior_; } @@ -194,7 +205,7 @@ } blink::WebURLLoader* BlinkPlatformImpl::createURLLoader() { - return new WebURLLoaderImpl(network_service_.get()); + return new WebURLLoaderImpl(network_service_.get(), &blob_registry_); } blink::WebSocketHandle* BlinkPlatformImpl::createWebSocketHandle() {
diff --git a/mojo/services/html_viewer/blink_platform_impl.h b/components/html_viewer/blink_platform_impl.h similarity index 84% rename from mojo/services/html_viewer/blink_platform_impl.h rename to components/html_viewer/blink_platform_impl.h index 12d23afb..3f9a803 100644 --- a/mojo/services/html_viewer/blink_platform_impl.h +++ b/components/html_viewer/blink_platform_impl.h
@@ -2,20 +2,21 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef MOJO_SERVICES_HTML_VIEWER_BLINK_PLATFORM_IMPL_H_ -#define MOJO_SERVICES_HTML_VIEWER_BLINK_PLATFORM_IMPL_H_ +#ifndef COMPONENTS_HTML_VIEWER_BLINK_PLATFORM_IMPL_H_ +#define COMPONENTS_HTML_VIEWER_BLINK_PLATFORM_IMPL_H_ #include "base/memory/scoped_ptr.h" #include "base/message_loop/message_loop.h" #include "base/threading/thread_local_storage.h" #include "base/timer/timer.h" #include "cc/blink/web_compositor_support_impl.h" +#include "components/html_viewer/blink_resource_map.h" +#include "components/html_viewer/mock_web_blob_registry_impl.h" +#include "components/html_viewer/web_mime_registry_impl.h" +#include "components/html_viewer/web_notification_manager_impl.h" +#include "components/html_viewer/web_scheduler_impl.h" +#include "components/html_viewer/web_theme_engine_impl.h" #include "components/webcrypto/webcrypto_impl.h" -#include "mojo/services/html_viewer/blink_resource_map.h" -#include "mojo/services/html_viewer/webmimeregistry_impl.h" -#include "mojo/services/html_viewer/webnotificationmanager_impl.h" -#include "mojo/services/html_viewer/webscheduler_impl.h" -#include "mojo/services/html_viewer/webthemeengine_impl.h" #include "mojo/services/network/public/interfaces/network_service.mojom.h" #include "third_party/WebKit/public/platform/Platform.h" #include "third_party/WebKit/public/platform/WebScrollbarBehavior.h" @@ -42,6 +43,7 @@ virtual blink::WebThemeEngine* themeEngine(); virtual blink::WebScheduler* scheduler(); virtual blink::WebString defaultLocale(); + virtual blink::WebBlobRegistry* blobRegistry(); virtual double currentTime(); virtual double monotonicallyIncreasingTime(); virtual void cryptographicallyRandomValues(unsigned char* buffer, @@ -52,6 +54,8 @@ virtual void callOnMainThread(void (*func)(void*), void* context); virtual bool isThreadedCompositingEnabled(); virtual blink::WebCompositorSupport* compositorSupport(); + void createMessageChannel(blink::WebMessagePortChannel** channel1, + blink::WebMessagePortChannel** channel2) override; virtual blink::WebURLLoader* createURLLoader(); virtual blink::WebSocketHandle* createWebSocketHandle(); virtual blink::WebString userAgent(); @@ -104,6 +108,7 @@ blink::WebScrollbarBehavior scrollbar_behavior_; BlinkResourceMap blink_resource_map_; mojo::NetworkServicePtr network_service_; + MockWebBlobRegistryImpl blob_registry_; scoped_ptr<WebCookieJarImpl> cookie_jar_; scoped_ptr<WebClipboardImpl> clipboard_; @@ -112,4 +117,4 @@ } // namespace html_viewer -#endif // MOJO_SERVICES_HTML_VIEWER_BLINK_PLATFORM_IMPL_H_ +#endif // COMPONENTS_HTML_VIEWER_BLINK_PLATFORM_IMPL_H_
diff --git a/mojo/services/html_viewer/blink_resource_constants.h b/components/html_viewer/blink_resource_constants.h similarity index 96% rename from mojo/services/html_viewer/blink_resource_constants.h rename to components/html_viewer/blink_resource_constants.h index 14d08d0..49c5a71b6 100644 --- a/mojo/services/html_viewer/blink_resource_constants.h +++ b/components/html_viewer/blink_resource_constants.h
@@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef MOJO_SERVICES_HTML_VIEWER_BLINK_RESOURCE_CONSTANTS_H_ -#define MOJO_SERVICES_HTML_VIEWER_BLINK_RESOURCE_CONSTANTS_H_ +#ifndef COMPONENTS_HTML_VIEWER_BLINK_RESOURCE_CONSTANTS_H_ +#define COMPONENTS_HTML_VIEWER_BLINK_RESOURCE_CONSTANTS_H_ #include "blink/public/resources/grit/blink_image_resources.h" #include "blink/public/resources/grit/blink_resources.h" @@ -124,4 +124,4 @@ } // namespace html_viewer -#endif // MOJO_SERVICES_HTML_VIEWER_BLINK_RESOURCE_CONSTANTS_H_ +#endif // COMPONENTS_HTML_VIEWER_BLINK_RESOURCE_CONSTANTS_H_
diff --git a/mojo/services/html_viewer/blink_url_request_type_converters.cc b/components/html_viewer/blink_url_request_type_converters.cc similarity index 97% rename from mojo/services/html_viewer/blink_url_request_type_converters.cc rename to components/html_viewer/blink_url_request_type_converters.cc index 3cae06f..29fbbe69 100644 --- a/mojo/services/html_viewer/blink_url_request_type_converters.cc +++ b/components/html_viewer/blink_url_request_type_converters.cc
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "mojo/services/html_viewer/blink_url_request_type_converters.h" +#include "components/html_viewer/blink_url_request_type_converters.h" #include "base/strings/string_util.h" #include "third_party/WebKit/public/platform/WebHTTPHeaderVisitor.h"
diff --git a/mojo/services/html_viewer/blink_url_request_type_converters.h b/components/html_viewer/blink_url_request_type_converters.h similarity index 69% rename from mojo/services/html_viewer/blink_url_request_type_converters.h rename to components/html_viewer/blink_url_request_type_converters.h index 43cc050..7812a7c 100644 --- a/mojo/services/html_viewer/blink_url_request_type_converters.h +++ b/components/html_viewer/blink_url_request_type_converters.h
@@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef MOJO_SERVICES_HTML_VIEWER_BLINK_URL_REQUEST_TYPE_CONVERTERS_H_ -#define MOJO_SERVICES_HTML_VIEWER_BLINK_URL_REQUEST_TYPE_CONVERTERS_H_ +#ifndef COMPONENTS_HTML_VIEWER_BLINK_URL_REQUEST_TYPE_CONVERTERS_H_ +#define COMPONENTS_HTML_VIEWER_BLINK_URL_REQUEST_TYPE_CONVERTERS_H_ #include "mojo/services/network/public/interfaces/url_loader.mojom.h" @@ -20,4 +20,4 @@ } // namespace mojo -#endif // MOJO_SERVICES_HTML_VIEWER_BLINK_URL_REQUEST_TYPE_CONVERTERS_H_ +#endif // COMPONENTS_HTML_VIEWER_BLINK_URL_REQUEST_TYPE_CONVERTERS_H_
diff --git a/mojo/services/html_viewer/discardable_memory_allocator.cc b/components/html_viewer/discardable_memory_allocator.cc similarity index 98% rename from mojo/services/html_viewer/discardable_memory_allocator.cc rename to components/html_viewer/discardable_memory_allocator.cc index a75ac63..f76deee 100644 --- a/mojo/services/html_viewer/discardable_memory_allocator.cc +++ b/components/html_viewer/discardable_memory_allocator.cc
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "mojo/services/html_viewer/discardable_memory_allocator.h" +#include "components/html_viewer/discardable_memory_allocator.h" #include "base/memory/discardable_memory.h" #include "base/memory/weak_ptr.h"
diff --git a/mojo/services/html_viewer/discardable_memory_allocator.h b/components/html_viewer/discardable_memory_allocator.h similarity index 90% rename from mojo/services/html_viewer/discardable_memory_allocator.h rename to components/html_viewer/discardable_memory_allocator.h index 73d6bcf..be7f063e 100644 --- a/mojo/services/html_viewer/discardable_memory_allocator.h +++ b/components/html_viewer/discardable_memory_allocator.h
@@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef MOJO_SERVICES_HTML_VIEWER_DISCARDABLE_MEMORY_ALLOCATOR_H_ -#define MOJO_SERVICES_HTML_VIEWER_DISCARDABLE_MEMORY_ALLOCATOR_H_ +#ifndef COMPONENTS_HTML_VIEWER_DISCARDABLE_MEMORY_ALLOCATOR_H_ +#define COMPONENTS_HTML_VIEWER_DISCARDABLE_MEMORY_ALLOCATOR_H_ #include <list> @@ -58,4 +58,4 @@ } // namespace html_viewer -#endif // MOJO_SERVICES_HTML_VIEWER_DISCARDABLE_MEMORY_ALLOCATOR_H_ +#endif // COMPONENTS_HTML_VIEWER_DISCARDABLE_MEMORY_ALLOCATOR_H_
diff --git a/mojo/services/html_viewer/discardable_memory_allocator_unittest.cc b/components/html_viewer/discardable_memory_allocator_unittest.cc similarity index 96% rename from mojo/services/html_viewer/discardable_memory_allocator_unittest.cc rename to components/html_viewer/discardable_memory_allocator_unittest.cc index d63d2c5..cc73fb0 100644 --- a/mojo/services/html_viewer/discardable_memory_allocator_unittest.cc +++ b/components/html_viewer/discardable_memory_allocator_unittest.cc
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "mojo/services/html_viewer/discardable_memory_allocator.h" +#include "components/html_viewer/discardable_memory_allocator.h" #include "base/memory/discardable_memory.h" #include "testing/gtest/include/gtest/gtest.h"
diff --git a/mojo/services/html_viewer/generate_blink_resource_map.py b/components/html_viewer/generate_blink_resource_map.py similarity index 93% rename from mojo/services/html_viewer/generate_blink_resource_map.py rename to components/html_viewer/generate_blink_resource_map.py index 0fdee71..e60be14 100644 --- a/mojo/services/html_viewer/generate_blink_resource_map.py +++ b/components/html_viewer/generate_blink_resource_map.py
@@ -10,7 +10,7 @@ try: grit_module_path = os.path.join( - os.path.dirname(__file__), '..', '..', '..', 'tools', 'grit') + os.path.dirname(__file__), '..', '..', 'tools', 'grit') sys.path.insert(0, grit_module_path) from grit.format import data_pack as DataPack except ImportError, e: @@ -22,8 +22,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef MOJO_SERVICES_HTML_VIEWER_BLINK_RESOURCE_MAP_H_ -#define MOJO_SERVICES_HTML_VIEWER_BLINK_RESOURCE_MAP_H_ +#ifndef COMPONENTS_HTML_VIEWER_BLINK_RESOURCE_MAP_H_ +#define COMPONENTS_HTML_VIEWER_BLINK_RESOURCE_MAP_H_ #include <map> @@ -54,7 +54,7 @@ }; } // namespace html_viewer -#endif // MOJO_SERVICES_HTML_VIEWER_BLINK_RESOURCE_MAP_H_""" +#endif // COMPONENTS_HTML_VIEWER_BLINK_RESOURCE_MAP_H_""" cpp_template = \ """// Copyright 2015 The Chromium Authors. All rights reserved.
diff --git a/mojo/services/html_viewer/html_document.cc b/components/html_viewer/html_document.cc similarity index 94% rename from mojo/services/html_viewer/html_document.cc rename to components/html_viewer/html_document.cc index 7074080a..e1b57cf 100644 --- a/mojo/services/html_viewer/html_document.cc +++ b/components/html_viewer/html_document.cc
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "mojo/services/html_viewer/html_document.h" +#include "components/html_viewer/html_document.h" #include "base/bind.h" #include "base/location.h" @@ -12,15 +12,15 @@ #include "base/stl_util.h" #include "base/strings/string_util.h" #include "base/thread_task_runner_handle.h" +#include "components/html_viewer/blink_input_events_type_converters.h" +#include "components/html_viewer/blink_url_request_type_converters.h" +#include "components/html_viewer/web_layer_tree_view_impl.h" +#include "components/html_viewer/web_media_player_factory.h" +#include "components/html_viewer/web_storage_namespace_impl.h" +#include "components/html_viewer/web_url_loader_impl.h" #include "media/blink/webencryptedmediaclient_impl.h" #include "media/cdm/default_cdm_factory.h" #include "media/filters/default_media_permission.h" -#include "mojo/services/html_viewer/blink_input_events_type_converters.h" -#include "mojo/services/html_viewer/blink_url_request_type_converters.h" -#include "mojo/services/html_viewer/weblayertreeview_impl.h" -#include "mojo/services/html_viewer/webmediaplayer_factory.h" -#include "mojo/services/html_viewer/webstoragenamespace_impl.h" -#include "mojo/services/html_viewer/weburlloader_impl.h" #include "skia/ext/refptr.h" #include "third_party/WebKit/public/platform/Platform.h" #include "third_party/WebKit/public/platform/WebHTTPHeaderVisitor.h" @@ -98,6 +98,12 @@ if (EqualsASCII(request.httpMethod(), "POST")) return true; + // Logging into Gmail fails when the referrer isn't sent with a request. + // TODO(jam): pass referrer and other HTTP data to NavigatorHost so we can + // use a new process in this case. + if (!request.httpHeaderField(blink::WebString::fromUTF8("Referer")).isEmpty()) + return true; + // Otherwise we don't know if we're the right app to handle this request. Ask // host to do the navigation for us. return false;
diff --git a/mojo/services/html_viewer/html_document.h b/components/html_viewer/html_document.h similarity index 95% rename from mojo/services/html_viewer/html_document.h rename to components/html_viewer/html_document.h index 49b83ba..4068888 100644 --- a/mojo/services/html_viewer/html_document.h +++ b/components/html_viewer/html_document.h
@@ -2,15 +2,15 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef MOJO_SERVICES_HTML_VIEWER_HTML_DOCUMENT_H_ -#define MOJO_SERVICES_HTML_VIEWER_HTML_DOCUMENT_H_ +#ifndef COMPONENTS_HTML_VIEWER_HTML_DOCUMENT_H_ +#define COMPONENTS_HTML_VIEWER_HTML_DOCUMENT_H_ #include <set> #include "base/callback.h" #include "base/macros.h" -#include "mojo/services/html_viewer/ax_provider_impl.h" -#include "mojo/services/html_viewer/touch_handler.h" +#include "components/html_viewer/ax_provider_impl.h" +#include "components/html_viewer/touch_handler.h" #include "mojo/services/network/public/interfaces/url_loader.mojom.h" #include "third_party/WebKit/public/web/WebFrameClient.h" #include "third_party/WebKit/public/web/WebSandboxFlags.h" @@ -174,4 +174,4 @@ } // namespace html_viewer -#endif // MOJO_SERVICES_HTML_VIEWER_HTML_DOCUMENT_H_ +#endif // COMPONENTS_HTML_VIEWER_HTML_DOCUMENT_H_
diff --git a/mojo/services/html_viewer/html_viewer.cc b/components/html_viewer/html_viewer.cc similarity index 97% rename from mojo/services/html_viewer/html_viewer.cc rename to components/html_viewer/html_viewer.cc index ad7b6ad..d71d043 100644 --- a/mojo/services/html_viewer/html_viewer.cc +++ b/components/html_viewer/html_viewer.cc
@@ -10,12 +10,12 @@ #include "base/path_service.h" #include "base/strings/utf_string_conversions.h" #include "base/threading/thread.h" +#include "components/html_viewer/blink_platform_impl.h" +#include "components/html_viewer/discardable_memory_allocator.h" +#include "components/html_viewer/html_document.h" +#include "components/html_viewer/web_media_player_factory.h" #include "gin/v8_initializer.h" #include "mojo/application/application_runner_chromium.h" -#include "mojo/services/html_viewer/blink_platform_impl.h" -#include "mojo/services/html_viewer/discardable_memory_allocator.h" -#include "mojo/services/html_viewer/html_document.h" -#include "mojo/services/html_viewer/webmediaplayer_factory.h" #include "mojo/services/network/public/interfaces/network_service.mojom.h" #include "third_party/WebKit/public/web/WebKit.h" #include "third_party/WebKit/public/web/WebRuntimeFeatures.h"
diff --git a/mojo/services/html_viewer/html_viewer_version.rc b/components/html_viewer/html_viewer_version.rc similarity index 100% rename from mojo/services/html_viewer/html_viewer_version.rc rename to components/html_viewer/html_viewer_version.rc
diff --git a/components/html_viewer/mock_web_blob_registry_impl.cc b/components/html_viewer/mock_web_blob_registry_impl.cc new file mode 100644 index 0000000..02cff57 --- /dev/null +++ b/components/html_viewer/mock_web_blob_registry_impl.cc
@@ -0,0 +1,121 @@ +// 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 "components/html_viewer/mock_web_blob_registry_impl.h" + +#include "third_party/WebKit/public/platform/WebBlobData.h" +#include "third_party/WebKit/public/platform/WebString.h" +#include "third_party/WebKit/public/platform/WebURL.h" +#include "third_party/WebKit/public/platform/WebVector.h" + +using blink::WebBlobData; +using blink::WebString; +using blink::WebURL; +using blink::WebVector; + +namespace html_viewer { + +MockWebBlobRegistryImpl::MockWebBlobRegistryImpl() { +} + +MockWebBlobRegistryImpl::~MockWebBlobRegistryImpl() { +} + +void MockWebBlobRegistryImpl::registerBlobData(const WebString& uuid, + const WebBlobData& data) { + const std::string uuid_str(uuid.utf8()); + blob_ref_count_map_[uuid_str] = 1; + scoped_ptr<ScopedVector<blink::WebBlobData::Item>> items( + new ScopedVector<blink::WebBlobData::Item>); + items->reserve(data.itemCount()); + for (size_t i = 0; i < data.itemCount(); ++i) { + scoped_ptr<blink::WebBlobData::Item> item(new blink::WebBlobData::Item); + data.itemAt(i, *item); + items->push_back(item.release()); + } + blob_data_items_map_.set(uuid_str, items.Pass()); +} + +void MockWebBlobRegistryImpl::addBlobDataRef(const WebString& uuid) { + blob_ref_count_map_[uuid.utf8()]++; +} + +void MockWebBlobRegistryImpl::removeBlobDataRef(const WebString& uuid) { + const std::string uuid_str(uuid.utf8()); + auto it = blob_ref_count_map_.find(uuid_str); + if (it != blob_ref_count_map_.end() && !--it->second) { + blob_data_items_map_.erase(uuid_str); + blob_ref_count_map_.erase(it); + } +} + +void MockWebBlobRegistryImpl::registerPublicBlobURL(const WebURL& url, + const WebString& uuid) { + public_url_to_uuid_[url.spec()] = uuid; + addBlobDataRef(uuid); +} + +void MockWebBlobRegistryImpl::revokePublicBlobURL(const WebURL& url) { + auto it = public_url_to_uuid_.find(url.spec()); + if (it != public_url_to_uuid_.end()) { + removeBlobDataRef(it->second); + public_url_to_uuid_.erase(it); + } +} + +void MockWebBlobRegistryImpl::registerStreamURL(const WebURL& url, + const WebString& content_type) { + NOTIMPLEMENTED(); +} + +void MockWebBlobRegistryImpl::registerStreamURL(const WebURL& url, + const blink::WebURL& src_url) { + NOTIMPLEMENTED(); +} + +void MockWebBlobRegistryImpl::addDataToStream(const WebURL& url, + const char* data, + size_t length) { + NOTIMPLEMENTED(); +} + +void MockWebBlobRegistryImpl::flushStream(const WebURL& url) { + NOTIMPLEMENTED(); +} + +void MockWebBlobRegistryImpl::finalizeStream(const WebURL& url) { + NOTIMPLEMENTED(); +} + +void MockWebBlobRegistryImpl::abortStream(const WebURL& url) { + NOTIMPLEMENTED(); +} + +void MockWebBlobRegistryImpl::unregisterStreamURL(const WebURL& url) { + NOTIMPLEMENTED(); +} + +bool MockWebBlobRegistryImpl::GetUUIDForURL(const blink::WebURL& url, + blink::WebString* uuid) const { + auto it = public_url_to_uuid_.find(url.spec()); + if (it != public_url_to_uuid_.end()) { + *uuid = it->second; + return true; + } + + return false; +} + +bool MockWebBlobRegistryImpl::GetBlobItems( + const WebString& uuid, + WebVector<WebBlobData::Item*>* items) const { + ScopedVector<WebBlobData::Item>* item_vector = + blob_data_items_map_.get(uuid.utf8()); + if (!item_vector) + return false; + *items = item_vector->get(); + return true; +} + +} // namespace html_viewer
diff --git a/components/html_viewer/mock_web_blob_registry_impl.h b/components/html_viewer/mock_web_blob_registry_impl.h new file mode 100644 index 0000000..8c8acbbb --- /dev/null +++ b/components/html_viewer/mock_web_blob_registry_impl.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 COMPONENTS_HTML_VIEWER_MOCK_WEB_BLOB_REGISTRY_IMPL_H_ +#define COMPONENTS_HTML_VIEWER_MOCK_WEB_BLOB_REGISTRY_IMPL_H_ + +#include <map> + +#include "base/containers/scoped_ptr_hash_map.h" +#include "base/macros.h" +#include "base/memory/scoped_vector.h" +#include "third_party/WebKit/public/platform/WebBlobData.h" +#include "third_party/WebKit/public/platform/WebBlobRegistry.h" +#include "third_party/WebKit/public/platform/WebVector.h" + +namespace html_viewer { + +// TODO(erg): For now, this is a just a copy of content's testing +// mock. Eventually, this should be turned into a real implementation, but this +// at least lets us get github working. +class MockWebBlobRegistryImpl : public blink::WebBlobRegistry { + public: + MockWebBlobRegistryImpl(); + virtual ~MockWebBlobRegistryImpl(); + + virtual void registerBlobData(const blink::WebString& uuid, + const blink::WebBlobData& data); + virtual void addBlobDataRef(const blink::WebString& uuid); + virtual void removeBlobDataRef(const blink::WebString& uuid); + virtual void registerPublicBlobURL(const blink::WebURL&, + const blink::WebString& uuid); + virtual void revokePublicBlobURL(const blink::WebURL&); + + // Additional support for Streams. + virtual void registerStreamURL(const blink::WebURL& url, + const blink::WebString& content_type); + virtual void registerStreamURL(const blink::WebURL& url, + const blink::WebURL& src_url); + virtual void addDataToStream(const blink::WebURL& url, + const char* data, + size_t length); + virtual void flushStream(const blink::WebURL& url); + virtual void finalizeStream(const blink::WebURL& url); + virtual void abortStream(const blink::WebURL& url); + virtual void unregisterStreamURL(const blink::WebURL& url); + + bool GetUUIDForURL(const blink::WebURL& url, blink::WebString* uuid) const; + bool GetBlobItems(const blink::WebString& uuid, + blink::WebVector<blink::WebBlobData::Item*>* items) const; + + private: + base::ScopedPtrHashMap<std::string, ScopedVector<blink::WebBlobData::Item>> + blob_data_items_map_; + std::map<std::string, int> blob_ref_count_map_; + + std::map<std::string, blink::WebString> public_url_to_uuid_; + + DISALLOW_COPY_AND_ASSIGN(MockWebBlobRegistryImpl); +}; + +} // namespace html_viewer + +#endif // COMPONENTS_HTML_VIEWER_MOCK_WEB_BLOB_REGISTRY_IMPL_H_
diff --git a/mojo/services/html_viewer/test_blink_platform_impl.cc b/components/html_viewer/test_blink_platform_impl.cc similarity index 88% rename from mojo/services/html_viewer/test_blink_platform_impl.cc rename to components/html_viewer/test_blink_platform_impl.cc index 8875c362..700502df 100644 --- a/mojo/services/html_viewer/test_blink_platform_impl.cc +++ b/components/html_viewer/test_blink_platform_impl.cc
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "mojo/services/html_viewer/test_blink_platform_impl.h" +#include "components/html_viewer/test_blink_platform_impl.h" namespace html_viewer {
diff --git a/mojo/services/html_viewer/test_blink_platform_impl.h b/components/html_viewer/test_blink_platform_impl.h similarity index 73% rename from mojo/services/html_viewer/test_blink_platform_impl.h rename to components/html_viewer/test_blink_platform_impl.h index 8790e4d..c3b6768 100644 --- a/mojo/services/html_viewer/test_blink_platform_impl.h +++ b/components/html_viewer/test_blink_platform_impl.h
@@ -2,10 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef MOJO_SERVICES_HTML_VIEWER_TEST_BLINK_PLATFORM_IMPL_H_ -#define MOJO_SERVICES_HTML_VIEWER_TEST_BLINK_PLATFORM_IMPL_H_ +#ifndef COMPONENTS_HTML_VIEWER_TEST_BLINK_PLATFORM_IMPL_H_ +#define COMPONENTS_HTML_VIEWER_TEST_BLINK_PLATFORM_IMPL_H_ -#include "mojo/services/html_viewer/blink_platform_impl.h" +#include "components/html_viewer/blink_platform_impl.h" #include "third_party/WebKit/public/platform/WebClipboard.h" #include "third_party/WebKit/public/platform/WebCookieJar.h" @@ -28,4 +28,4 @@ } // namespace html_viewer -#endif // MOJO_SERVICES_HTML_VIEWER_TEST_BLINK_PLATFORM_IMPL_H_ +#endif // COMPONENTS_HTML_VIEWER_TEST_BLINK_PLATFORM_IMPL_H_
diff --git a/mojo/services/html_viewer/touch_handler.cc b/components/html_viewer/touch_handler.cc similarity index 98% rename from mojo/services/html_viewer/touch_handler.cc rename to components/html_viewer/touch_handler.cc index 6261dab..e968e602 100644 --- a/mojo/services/html_viewer/touch_handler.cc +++ b/components/html_viewer/touch_handler.cc
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "mojo/services/html_viewer/touch_handler.h" +#include "components/html_viewer/touch_handler.h" #include "third_party/WebKit/public/web/WebInputEvent.h" #include "third_party/WebKit/public/web/WebView.h"
diff --git a/mojo/services/html_viewer/touch_handler.h b/components/html_viewer/touch_handler.h similarity index 90% rename from mojo/services/html_viewer/touch_handler.h rename to components/html_viewer/touch_handler.h index a4a8fbb6..460a3d5 100644 --- a/mojo/services/html_viewer/touch_handler.h +++ b/components/html_viewer/touch_handler.h
@@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef MOJO_SERVICES_HTML_VIEWER_TOUCH_HANDLER_H_ -#define MOJO_SERVICES_HTML_VIEWER_TOUCH_HANDLER_H_ +#ifndef COMPONENTS_HTML_VIEWER_TOUCH_HANDLER_H_ +#define COMPONENTS_HTML_VIEWER_TOUCH_HANDLER_H_ #include "base/basictypes.h" #include "ui/events/gesture_detection/filtered_gesture_provider.h" @@ -59,4 +59,4 @@ } // namespace html_viewer -#endif // MOJO_SERVICES_HTML_VIEWER_TOUCH_HANDLER_H_ +#endif // COMPONENTS_HTML_VIEWER_TOUCH_HANDLER_H_
diff --git a/mojo/services/html_viewer/webclipboard_impl.cc b/components/html_viewer/web_clipboard_impl.cc similarity index 97% rename from mojo/services/html_viewer/webclipboard_impl.cc rename to components/html_viewer/web_clipboard_impl.cc index 05c0be4..8810c3d0 100644 --- a/mojo/services/html_viewer/webclipboard_impl.cc +++ b/components/html_viewer/web_clipboard_impl.cc
@@ -2,10 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "mojo/services/html_viewer/webclipboard_impl.h" +#include "components/html_viewer/web_clipboard_impl.h" #include "base/bind.h" -#include "mojo/services/html_viewer/blink_basic_type_converters.h" +#include "components/html_viewer/blink_basic_type_converters.h" using mojo::Array; using mojo::Clipboard;
diff --git a/mojo/services/html_viewer/webclipboard_impl.h b/components/html_viewer/web_clipboard_impl.h similarity index 90% rename from mojo/services/html_viewer/webclipboard_impl.h rename to components/html_viewer/web_clipboard_impl.h index 8109506..f671e31 100644 --- a/mojo/services/html_viewer/webclipboard_impl.h +++ b/components/html_viewer/web_clipboard_impl.h
@@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef MOJO_SERVICES_HTML_VIEWER_WEBCLIPBOARD_IMPL_H_ -#define MOJO_SERVICES_HTML_VIEWER_WEBCLIPBOARD_IMPL_H_ +#ifndef COMPONENTS_HTML_VIEWER_WEB_CLIPBOARD_IMPL_H_ +#define COMPONENTS_HTML_VIEWER_WEB_CLIPBOARD_IMPL_H_ #include "third_party/WebKit/public/platform/WebClipboard.h" #include "third_party/mojo_services/src/clipboard/public/interfaces/clipboard.mojom.h" @@ -46,4 +46,4 @@ } // namespace html_viewer -#endif // MOJO_SERVICES_HTML_VIEWER_WEBCLIPBOARD_IMPL_H_ +#endif // COMPONENTS_HTML_VIEWER_WEB_CLIPBOARD_IMPL_H_
diff --git a/mojo/services/html_viewer/webcookiejar_impl.cc b/components/html_viewer/web_cookie_jar_impl.cc similarity index 96% rename from mojo/services/html_viewer/webcookiejar_impl.cc rename to components/html_viewer/web_cookie_jar_impl.cc index 0b4b012..019acfb 100644 --- a/mojo/services/html_viewer/webcookiejar_impl.cc +++ b/components/html_viewer/web_cookie_jar_impl.cc
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "mojo/services/html_viewer/webcookiejar_impl.h" +#include "components/html_viewer/web_cookie_jar_impl.h" #include "base/bind.h" #include "third_party/WebKit/public/platform/WebURL.h"
diff --git a/mojo/services/html_viewer/webcookiejar_impl.h b/components/html_viewer/web_cookie_jar_impl.h similarity index 85% rename from mojo/services/html_viewer/webcookiejar_impl.h rename to components/html_viewer/web_cookie_jar_impl.h index 052a45f..bec897e 100644 --- a/mojo/services/html_viewer/webcookiejar_impl.h +++ b/components/html_viewer/web_cookie_jar_impl.h
@@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef MOJO_SERVICES_HTML_VIEWER_WEBCOOKIEJAR_IMPL_H_ -#define MOJO_SERVICES_HTML_VIEWER_WEBCOOKIEJAR_IMPL_H_ +#ifndef COMPONENTS_HTML_VIEWER_WEB_COOKIE_JAR_IMPL_H_ +#define COMPONENTS_HTML_VIEWER_WEB_COOKIE_JAR_IMPL_H_ #include "mojo/services/network/public/interfaces/cookie_store.mojom.h" #include "third_party/WebKit/public/platform/WebCookieJar.h" @@ -33,4 +33,4 @@ } // namespace html_viewer -#endif // MOJO_SERVICES_HTML_VIEWER_WEBCOOKIEJAR_IMPL_H_ +#endif // COMPONENTS_HTML_VIEWER_WEB_COOKIE_JAR_IMPL_H_
diff --git a/mojo/services/html_viewer/weblayertreeview_impl.cc b/components/html_viewer/web_layer_tree_view_impl.cc similarity index 98% rename from mojo/services/html_viewer/weblayertreeview_impl.cc rename to components/html_viewer/web_layer_tree_view_impl.cc index fb73ba3d..94f1485 100644 --- a/mojo/services/html_viewer/weblayertreeview_impl.cc +++ b/components/html_viewer/web_layer_tree_view_impl.cc
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "mojo/services/html_viewer/weblayertreeview_impl.h" +#include "components/html_viewer/web_layer_tree_view_impl.h" #include "base/message_loop/message_loop_proxy.h" #include "cc/blink/web_layer_impl.h"
diff --git a/mojo/services/html_viewer/weblayertreeview_impl.h b/components/html_viewer/web_layer_tree_view_impl.h similarity index 96% rename from mojo/services/html_viewer/weblayertreeview_impl.h rename to components/html_viewer/web_layer_tree_view_impl.h index 32e36a4..b2c352a2 100644 --- a/mojo/services/html_viewer/weblayertreeview_impl.h +++ b/components/html_viewer/web_layer_tree_view_impl.h
@@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef MOJO_SERVICES_HTML_VIEWER_WEBLAYERTREEVIEW_IMPL_H_ -#define MOJO_SERVICES_HTML_VIEWER_WEBLAYERTREEVIEW_IMPL_H_ +#ifndef COMPONENTS_HTML_VIEWER_WEB_LAYER_TREE_VIEW_IMPL_H_ +#define COMPONENTS_HTML_VIEWER_WEB_LAYER_TREE_VIEW_IMPL_H_ #include "base/macros.h" #include "base/memory/ref_counted.h" @@ -133,4 +133,4 @@ } // namespace html_viewer -#endif // MOJO_SERVICES_HTML_VIEWER_WEBLAYERTREEVIEW_IMPL_H_ +#endif // COMPONENTS_HTML_VIEWER_WEB_LAYER_TREE_VIEW_IMPL_H_
diff --git a/mojo/services/html_viewer/webmediaplayer_factory.cc b/components/html_viewer/web_media_player_factory.cc similarity index 98% rename from mojo/services/html_viewer/webmediaplayer_factory.cc rename to components/html_viewer/web_media_player_factory.cc index e7cec99..0de85f3 100644 --- a/mojo/services/html_viewer/webmediaplayer_factory.cc +++ b/components/html_viewer/web_media_player_factory.cc
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "mojo/services/html_viewer/webmediaplayer_factory.h" +#include "components/html_viewer/web_media_player_factory.h" #include "base/bind.h" #include "base/files/file_path.h"
diff --git a/mojo/services/html_viewer/webmediaplayer_factory.h b/components/html_viewer/web_media_player_factory.h similarity index 91% rename from mojo/services/html_viewer/webmediaplayer_factory.h rename to components/html_viewer/web_media_player_factory.h index 723b54f..360a696 100644 --- a/mojo/services/html_viewer/webmediaplayer_factory.h +++ b/components/html_viewer/web_media_player_factory.h
@@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef MOJO_SERVICES_HTML_VIEWER_WEBMEDIAPLAYER_FACTORY_H_ -#define MOJO_SERVICES_HTML_VIEWER_WEBMEDIAPLAYER_FACTORY_H_ +#ifndef COMPONENTS_HTML_VIEWER_WEB_MEDIA_PLAYER_FACTORY_H_ +#define COMPONENTS_HTML_VIEWER_WEB_MEDIA_PLAYER_FACTORY_H_ #include "base/macros.h" #include "base/memory/ref_counted.h" @@ -73,4 +73,4 @@ } // namespace html_viewer -#endif // MOJO_SERVICES_HTML_VIEWER_WEBMEDIAPLAYER_FACTORY_H_ +#endif // COMPONENTS_HTML_VIEWER_WEB_MEDIA_PLAYER_FACTORY_H_
diff --git a/components/html_viewer/web_message_port_channel_impl.cc b/components/html_viewer/web_message_port_channel_impl.cc new file mode 100644 index 0000000..45e75fc --- /dev/null +++ b/components/html_viewer/web_message_port_channel_impl.cc
@@ -0,0 +1,132 @@ +// 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 "components/html_viewer/web_message_port_channel_impl.h" + +#include "base/bind.h" +#include "base/logging.h" +#include "base/strings/string16.h" +#include "third_party/WebKit/public/platform/WebMessagePortChannelClient.h" +#include "third_party/WebKit/public/platform/WebString.h" +#include "third_party/mojo/src/mojo/public/cpp/system/message_pipe.h" + +using blink::WebMessagePortChannel; +using blink::WebMessagePortChannelArray; +using blink::WebMessagePortChannelClient; +using blink::WebString; + +namespace html_viewer { + +void WebMessagePortChannelImpl::CreatePair( + blink::WebMessagePortChannel** channel1, + blink::WebMessagePortChannel** channel2) { + mojo::ScopedMessagePipeHandle pipe1; + mojo::ScopedMessagePipeHandle pipe2; + MojoResult result = mojo::CreateMessagePipe(nullptr, &pipe1, &pipe2); + if (result != MOJO_RESULT_OK) { + NOTREACHED(); + return; + } + + *channel1 = new WebMessagePortChannelImpl(pipe1.Pass());; + *channel2 = new WebMessagePortChannelImpl(pipe2.Pass()); +} + +WebMessagePortChannelImpl::WebMessagePortChannelImpl( + mojo::ScopedMessagePipeHandle pipe) + : client_(nullptr), pipe_(pipe.Pass()) { + WaitForNextMessage(); +} + +WebMessagePortChannelImpl::~WebMessagePortChannelImpl() { +} + +void WebMessagePortChannelImpl::setClient(WebMessagePortChannelClient* client) { + client_ = client; +} + +void WebMessagePortChannelImpl::destroy() { + setClient(nullptr); + delete this; +} + +void WebMessagePortChannelImpl::postMessage( + const WebString& message_as_string, + WebMessagePortChannelArray* channels) { + base::string16 string = message_as_string; + + std::vector<MojoHandle> handles; + if (channels) { + for (size_t i = 0; i < channels->size(); ++i) { + WebMessagePortChannelImpl* channel = + static_cast<WebMessagePortChannelImpl*>((*channels)[i]); + handles.push_back(channel->pipe_.release().value()); + channel->handle_watcher_.Stop(); + } + delete channels; + } + + uint32_t num_handles = static_cast<uint32_t>(handles.size()); + MojoHandle* handles_ptr = handles.empty() ? nullptr : &handles[0]; + + MojoResult result = MojoWriteMessage( + pipe_.get().value(), string.c_str(), + static_cast<uint32_t>(string.length() * sizeof(base::char16)), + handles_ptr, num_handles, MOJO_WRITE_MESSAGE_FLAG_NONE); + DCHECK_EQ(MOJO_RESULT_OK, result); +} + +bool WebMessagePortChannelImpl::tryGetMessage( + WebString* message, + WebMessagePortChannelArray& channels) { + uint32_t num_bytes = 0; + uint32_t num_handles = 0; + MojoResult result = MojoReadMessage( + pipe_.get().value(), nullptr, &num_bytes, nullptr, &num_handles, + MOJO_READ_MESSAGE_FLAG_NONE); + if (result != MOJO_RESULT_RESOURCE_EXHAUSTED) + return false; + + base::string16 message16; + CHECK(num_bytes % sizeof(base::char16) == 0); + message16.resize(num_bytes / sizeof(base::char16)); + std::vector<MojoHandle> handles; + handles.resize(num_handles); + + MojoHandle* handles_ptr = handles.empty() ? nullptr : &handles[0]; + result = MojoReadMessage( + pipe_.get().value(), &message16[0], &num_bytes, handles_ptr, &num_handles, + MOJO_READ_MESSAGE_FLAG_NONE); + if (result != MOJO_RESULT_OK) { + NOTREACHED(); + return false; + } + + *message = message16; + WebMessagePortChannelArray ports(handles.size()); + for (size_t i = 0; i < handles.size(); ++i) { + mojo::MessagePipeHandle mph(handles[i]); + mojo::ScopedMessagePipeHandle handle(mph); + ports[i] = new WebMessagePortChannelImpl(handle.Pass()); + } + channels = ports; + return true; +} + +void WebMessagePortChannelImpl::WaitForNextMessage() { + handle_watcher_.Start( + pipe_.get(), + MOJO_HANDLE_SIGNAL_READABLE, + MOJO_DEADLINE_INDEFINITE, + base::Bind(&WebMessagePortChannelImpl::OnMessageAvailable, + base::Unretained(this))); +} + +void WebMessagePortChannelImpl::OnMessageAvailable(MojoResult result) { + DCHECK_EQ(MOJO_RESULT_OK, result); + client_->messageAvailable(); + WaitForNextMessage(); +} + +} // namespace html_viewer
diff --git a/components/html_viewer/web_message_port_channel_impl.h b/components/html_viewer/web_message_port_channel_impl.h new file mode 100644 index 0000000..8c723bc --- /dev/null +++ b/components/html_viewer/web_message_port_channel_impl.h
@@ -0,0 +1,45 @@ +// 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 COMPONENTS_HTML_VIEWER_WEB_MESSAGE_PORT_CHANNEL_IMPL_H_ +#define COMPONENTS_HTML_VIEWER_WEB_MESSAGE_PORT_CHANNEL_IMPL_H_ + +#include "base/basictypes.h" +#include "mojo/common/handle_watcher.h" +#include "third_party/WebKit/public/platform/WebMessagePortChannel.h" +#include "third_party/mojo/src/mojo/public/cpp/system/message_pipe.h" + +namespace html_viewer { + +class WebMessagePortChannelImpl : public blink::WebMessagePortChannel { + public: + static void CreatePair( + blink::WebMessagePortChannel** channel1, + blink::WebMessagePortChannel** channel2); + + private: + explicit WebMessagePortChannelImpl(mojo::ScopedMessagePipeHandle pipe); + virtual ~WebMessagePortChannelImpl(); + + // blink::WebMessagePortChannel implementation. + virtual void setClient(blink::WebMessagePortChannelClient* client); + virtual void destroy(); + virtual void postMessage(const blink::WebString& message, + blink::WebMessagePortChannelArray* channels); + virtual bool tryGetMessage(blink::WebString* message, + blink::WebMessagePortChannelArray& channels); + + void WaitForNextMessage(); + void OnMessageAvailable(MojoResult result); + + blink::WebMessagePortChannelClient* client_; + mojo::ScopedMessagePipeHandle pipe_; + mojo::common::HandleWatcher handle_watcher_; + + DISALLOW_COPY_AND_ASSIGN(WebMessagePortChannelImpl); +}; + +} // namespace html_viewer + +#endif // COMPONENTS_HTML_VIEWER_WEB_MESSAGE_PORT_CHANNEL_IMPL_H_
diff --git a/mojo/services/html_viewer/webmimeregistry_impl.cc b/components/html_viewer/web_mime_registry_impl.cc similarity index 98% rename from mojo/services/html_viewer/webmimeregistry_impl.cc rename to components/html_viewer/web_mime_registry_impl.cc index 9878e87..46c7c03 100644 --- a/mojo/services/html_viewer/webmimeregistry_impl.cc +++ b/components/html_viewer/web_mime_registry_impl.cc
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "mojo/services/html_viewer/webmimeregistry_impl.h" +#include "components/html_viewer/web_mime_registry_impl.h" #include "base/files/file_path.h" #include "base/strings/string_util.h"
diff --git a/mojo/services/html_viewer/webmimeregistry_impl.h b/components/html_viewer/web_mime_registry_impl.h similarity index 89% rename from mojo/services/html_viewer/webmimeregistry_impl.h rename to components/html_viewer/web_mime_registry_impl.h index 87c0156..17f7f1b 100644 --- a/mojo/services/html_viewer/webmimeregistry_impl.h +++ b/components/html_viewer/web_mime_registry_impl.h
@@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef MOJO_SERVICES_HTML_VIEWER_WEBMIMEREGISTRY_IMPL_H_ -#define MOJO_SERVICES_HTML_VIEWER_WEBMIMEREGISTRY_IMPL_H_ +#ifndef COMPONENTS_HTML_VIEWER_WEB_MIME_REGISTRY_IMPL_H_ +#define COMPONENTS_HTML_VIEWER_WEB_MIME_REGISTRY_IMPL_H_ #include "base/compiler_specific.h" #include "third_party/WebKit/public/platform/WebMimeRegistry.h" @@ -41,4 +41,4 @@ } // namespace html_viewer -#endif // MOJO_SERVICES_HTML_VIEWER_WEBMIMEREGISTRY_IMPL_H_ +#endif // COMPONENTS_HTML_VIEWER_WEB_MIME_REGISTRY_IMPL_H_
diff --git a/mojo/services/html_viewer/webnotificationmanager_impl.cc b/components/html_viewer/web_notification_manager_impl.cc similarity index 95% rename from mojo/services/html_viewer/webnotificationmanager_impl.cc rename to components/html_viewer/web_notification_manager_impl.cc index fabca5e..c597ec24 100644 --- a/mojo/services/html_viewer/webnotificationmanager_impl.cc +++ b/components/html_viewer/web_notification_manager_impl.cc
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "mojo/services/html_viewer/webnotificationmanager_impl.h" +#include "components/html_viewer/web_notification_manager_impl.h" #include "base/logging.h"
diff --git a/mojo/services/html_viewer/webnotificationmanager_impl.h b/components/html_viewer/web_notification_manager_impl.h similarity index 89% rename from mojo/services/html_viewer/webnotificationmanager_impl.h rename to components/html_viewer/web_notification_manager_impl.h index 0cf178a..1110aca 100644 --- a/mojo/services/html_viewer/webnotificationmanager_impl.h +++ b/components/html_viewer/web_notification_manager_impl.h
@@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef MOJO_SERVICES_HTML_VIEWER_WEBNOTIFICATIONMANAGER_IMPL_H_ -#define MOJO_SERVICES_HTML_VIEWER_WEBNOTIFICATIONMANAGER_IMPL_H_ +#ifndef COMPONENTS_HTML_VIEWER_WEB_NOTIFICATION_MANAGER_IMPL_H_ +#define COMPONENTS_HTML_VIEWER_WEB_NOTIFICATION_MANAGER_IMPL_H_ #include "third_party/WebKit/public/platform/modules/notifications/WebNotificationManager.h" @@ -43,4 +43,4 @@ } // namespace html_viewer -#endif // MOJO_SERVICES_HTML_VIEWER_WEBNOTIFICATIONMANAGER_IMPL_H_ +#endif // COMPONENTS_HTML_VIEWER_WEB_NOTIFICATION_MANAGER_IMPL_H_
diff --git a/mojo/services/html_viewer/webscheduler_impl.cc b/components/html_viewer/web_scheduler_impl.cc similarity index 97% rename from mojo/services/html_viewer/webscheduler_impl.cc rename to components/html_viewer/web_scheduler_impl.cc index f414cb6..7f1ada5c 100644 --- a/mojo/services/html_viewer/webscheduler_impl.cc +++ b/components/html_viewer/web_scheduler_impl.cc
@@ -8,7 +8,7 @@ #include "base/bind.h" #include "base/message_loop/message_loop.h" #include "base/tracked_objects.h" -#include "mojo/services/html_viewer/webscheduler_impl.h" +#include "components/html_viewer/web_scheduler_impl.h" namespace html_viewer {
diff --git a/mojo/services/html_viewer/webscheduler_impl.h b/components/html_viewer/web_scheduler_impl.h similarity index 88% rename from mojo/services/html_viewer/webscheduler_impl.h rename to components/html_viewer/web_scheduler_impl.h index 112e7ee1..e0d754f 100644 --- a/mojo/services/html_viewer/webscheduler_impl.h +++ b/components/html_viewer/web_scheduler_impl.h
@@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef MOJO_SERVICES_HTML_VIEWER_WEBSCHEDULER_IMPL_H_ -#define MOJO_SERVICES_HTML_VIEWER_WEBSCHEDULER_IMPL_H_ +#ifndef COMPONENTS_HTML_VIEWER_WEB_SCHEDULER_IMPL_H_ +#define COMPONENTS_HTML_VIEWER_WEB_SCHEDULER_IMPL_H_ #include "third_party/WebKit/public/platform/WebScheduler.h" #include "third_party/WebKit/public/platform/WebThread.h" @@ -37,4 +37,4 @@ } // namespace html_viewer -#endif // MOJO_SERVICES_HTML_VIEWER_WEBSCHEDULER_IMPL_H_ +#endif // COMPONENTS_HTML_VIEWER_WEB_SCHEDULER_IMPL_H_
diff --git a/mojo/services/html_viewer/websockethandle_impl.cc b/components/html_viewer/web_socket_handle_impl.cc similarity index 97% rename from mojo/services/html_viewer/websockethandle_impl.cc rename to components/html_viewer/web_socket_handle_impl.cc index 2138546..59d9160bf 100644 --- a/mojo/services/html_viewer/websockethandle_impl.cc +++ b/components/html_viewer/web_socket_handle_impl.cc
@@ -2,14 +2,14 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "mojo/services/html_viewer/websockethandle_impl.h" +#include "components/html_viewer/web_socket_handle_impl.h" #include <vector> #include "base/bind.h" #include "base/macros.h" #include "base/memory/scoped_vector.h" -#include "mojo/services/html_viewer/blink_basic_type_converters.h" +#include "components/html_viewer/blink_basic_type_converters.h" #include "mojo/services/network/public/cpp/web_socket_read_queue.h" #include "mojo/services/network/public/cpp/web_socket_write_queue.h" #include "mojo/services/network/public/interfaces/network_service.mojom.h"
diff --git a/mojo/services/html_viewer/websockethandle_impl.h b/components/html_viewer/web_socket_handle_impl.h similarity index 91% rename from mojo/services/html_viewer/websockethandle_impl.h rename to components/html_viewer/web_socket_handle_impl.h index 4796f4d1c..0456228b 100644 --- a/mojo/services/html_viewer/websockethandle_impl.h +++ b/components/html_viewer/web_socket_handle_impl.h
@@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef MOJO_SERVICES_HTML_VIEWER_WEBSOCKETHANDLE_IMPL_H_ -#define MOJO_SERVICES_HTML_VIEWER_WEBSOCKETHANDLE_IMPL_H_ +#ifndef COMPONENTS_HTML_VIEWER_WEB_SOCKET_HANDLE_IMPL_H_ +#define COMPONENTS_HTML_VIEWER_WEB_SOCKET_HANDLE_IMPL_H_ #include "base/macros.h" #include "base/memory/scoped_ptr.h" @@ -60,4 +60,4 @@ } // namespace html_viewer -#endif // MOJO_SERVICES_HTML_VIEWER_WEBSOCKETHANDLE_IMPL_H_ +#endif // COMPONENTS_HTML_VIEWER_WEB_SOCKET_HANDLE_IMPL_H_
diff --git a/mojo/services/html_viewer/webstoragenamespace_impl.cc b/components/html_viewer/web_storage_namespace_impl.cc similarity index 93% rename from mojo/services/html_viewer/webstoragenamespace_impl.cc rename to components/html_viewer/web_storage_namespace_impl.cc index 47d7918..f61a63f 100644 --- a/mojo/services/html_viewer/webstoragenamespace_impl.cc +++ b/components/html_viewer/web_storage_namespace_impl.cc
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "mojo/services/html_viewer/webstoragenamespace_impl.h" +#include "components/html_viewer/web_storage_namespace_impl.h" #include <stdio.h>
diff --git a/mojo/services/html_viewer/webstoragenamespace_impl.h b/components/html_viewer/web_storage_namespace_impl.h similarity index 79% rename from mojo/services/html_viewer/webstoragenamespace_impl.h rename to components/html_viewer/web_storage_namespace_impl.h index 32e17c0..1c199aa 100644 --- a/mojo/services/html_viewer/webstoragenamespace_impl.h +++ b/components/html_viewer/web_storage_namespace_impl.h
@@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef MOJO_SERVICES_HTML_VIEWER_WEBSTORAGENAMESPACE_IMPL_H_ -#define MOJO_SERVICES_HTML_VIEWER_WEBSTORAGENAMESPACE_IMPL_H_ +#ifndef COMPONENTS_HTML_VIEWER_WEB_STORAGE_NAMESPACE_IMPL_H_ +#define COMPONENTS_HTML_VIEWER_WEB_STORAGE_NAMESPACE_IMPL_H_ #include "base/macros.h" #include "third_party/WebKit/public/platform/WebStorageNamespace.h" @@ -27,4 +27,4 @@ } // namespace html_viewer -#endif // MOJO_SERVICES_HTML_VIEWER_WEBSTORAGENAMESPACE_IMPL_H_ +#endif // COMPONENTS_HTML_VIEWER_WEB_STORAGE_NAMESPACE_IMPL_H_
diff --git a/mojo/services/html_viewer/webthemeengine_impl.cc b/components/html_viewer/web_theme_engine_impl.cc similarity index 98% rename from mojo/services/html_viewer/webthemeengine_impl.cc rename to components/html_viewer/web_theme_engine_impl.cc index c91462cb..69518ee1 100644 --- a/mojo/services/html_viewer/webthemeengine_impl.cc +++ b/components/html_viewer/web_theme_engine_impl.cc
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "mojo/services/html_viewer/webthemeengine_impl.h" +#include "components/html_viewer/web_theme_engine_impl.h" #include "skia/ext/platform_canvas.h" #include "third_party/WebKit/public/platform/WebRect.h"
diff --git a/mojo/services/html_viewer/webthemeengine_impl.h b/components/html_viewer/web_theme_engine_impl.h similarity index 86% rename from mojo/services/html_viewer/webthemeengine_impl.h rename to components/html_viewer/web_theme_engine_impl.h index 58e56ab7..e146e9d 100644 --- a/mojo/services/html_viewer/webthemeengine_impl.h +++ b/components/html_viewer/web_theme_engine_impl.h
@@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef MOJO_SERVICES_HTML_VIEWER_WEBTHEMEENGINE_IMPL_H_ -#define MOJO_SERVICES_HTML_VIEWER_WEBTHEMEENGINE_IMPL_H_ +#ifndef COMPONENTS_HTML_VIEWER_WEB_THEME_ENGINE_IMPL_H_ +#define COMPONENTS_HTML_VIEWER_WEB_THEME_ENGINE_IMPL_H_ #include "third_party/WebKit/public/platform/WebThemeEngine.h" @@ -28,4 +28,4 @@ } // namespace html_viewer -#endif // MOJO_SERVICES_HTML_VIEWER_WEBTHEMEENGINE_IMPL_H_ +#endif // COMPONENTS_HTML_VIEWER_WEB_THEME_ENGINE_IMPL_H_
diff --git a/mojo/services/html_viewer/webthread_impl.cc b/components/html_viewer/web_thread_impl.cc similarity index 98% rename from mojo/services/html_viewer/webthread_impl.cc rename to components/html_viewer/web_thread_impl.cc index a6f6f64..6f3bb53 100644 --- a/mojo/services/html_viewer/webthread_impl.cc +++ b/components/html_viewer/web_thread_impl.cc
@@ -5,7 +5,7 @@ // An implementation of WebThread in terms of base::MessageLoop and // base::Thread -#include "mojo/services/html_viewer/webthread_impl.h" +#include "components/html_viewer/web_thread_impl.h" #include "base/bind.h" #include "base/bind_helpers.h"
diff --git a/mojo/services/html_viewer/webthread_impl.h b/components/html_viewer/web_thread_impl.h similarity index 91% rename from mojo/services/html_viewer/webthread_impl.h rename to components/html_viewer/web_thread_impl.h index e7b499d..b9fc8044 100644 --- a/mojo/services/html_viewer/webthread_impl.h +++ b/components/html_viewer/web_thread_impl.h
@@ -2,14 +2,14 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef MOJO_SERVICES_HTML_VIEWER_WEBTHREAD_IMPL_H_ -#define MOJO_SERVICES_HTML_VIEWER_WEBTHREAD_IMPL_H_ +#ifndef COMPONENTS_HTML_VIEWER_WEB_THREAD_IMPL_H_ +#define COMPONENTS_HTML_VIEWER_WEB_THREAD_IMPL_H_ #include <map> #include "base/memory/scoped_ptr.h" #include "base/threading/thread.h" -#include "mojo/services/html_viewer/webscheduler_impl.h" +#include "components/html_viewer/web_scheduler_impl.h" #include "third_party/WebKit/public/platform/WebThread.h" namespace html_viewer { @@ -85,4 +85,4 @@ } // namespace html_viewer -#endif // MOJO_SERVICES_HTML_VIEWER_WEBTHREAD_IMPL_H_ +#endif // COMPONENTS_HTML_VIEWER_WEB_THREAD_IMPL_H_
diff --git a/mojo/services/html_viewer/weburlloader_impl.cc b/components/html_viewer/web_url_loader_impl.cc similarity index 78% rename from mojo/services/html_viewer/weburlloader_impl.cc rename to components/html_viewer/web_url_loader_impl.cc index f57e3f4b..b4874b2f 100644 --- a/mojo/services/html_viewer/weburlloader_impl.cc +++ b/components/html_viewer/web_url_loader_impl.cc
@@ -2,15 +2,15 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "mojo/services/html_viewer/weburlloader_impl.h" +#include "components/html_viewer/web_url_loader_impl.h" #include "base/bind.h" #include "base/logging.h" #include "base/strings/string_util.h" #include "base/thread_task_runner_handle.h" +#include "components/html_viewer/blink_url_request_type_converters.h" #include "mojo/common/common_type_converters.h" #include "mojo/common/url_type_converters.h" -#include "mojo/services/html_viewer/blink_url_request_type_converters.h" #include "mojo/services/network/public/interfaces/network_service.mojom.h" #include "net/base/net_errors.h" #include "third_party/WebKit/public/platform/WebURLError.h" @@ -18,6 +18,7 @@ #include "third_party/WebKit/public/platform/WebURLLoaderClient.h" #include "third_party/WebKit/public/platform/WebURLResponse.h" +using blink::WebString; using mojo::URLResponsePtr; namespace html_viewer { @@ -79,8 +80,12 @@ WebURLRequestExtraData::~WebURLRequestExtraData() { } -WebURLLoaderImpl::WebURLLoaderImpl(mojo::NetworkService* network_service) - : client_(NULL), weak_factory_(this) { +WebURLLoaderImpl::WebURLLoaderImpl(mojo::NetworkService* network_service, + MockWebBlobRegistryImpl* web_blob_registry) + : client_(NULL), + web_blob_registry_(web_blob_registry), + referrer_policy_(blink::WebReferrerPolicyDefault), + weak_factory_(this) { network_service->CreateURLLoader(GetProxy(&url_loader_)); } @@ -102,6 +107,10 @@ mojo::URLRequestPtr url_request = mojo::URLRequest::From(request); url_request->auto_follow_redirects = false; + referrer_policy_ = request.referrerPolicy(); + GURL referrer_url( + request.httpHeaderField(WebString::fromUTF8("Referer")).latin1()); + url_request->referrer = referrer_url.spec(); if (request.extraData()) { WebURLRequestExtraData* extra_data = @@ -112,12 +121,24 @@ weak_factory_.GetWeakPtr(), request, base::Passed(&extra_data->synthetic_response))); - } else { - url_loader_->Start(url_request.Pass(), - base::Bind(&WebURLLoaderImpl::OnReceivedResponse, - weak_factory_.GetWeakPtr(), - request)); + return; } + + blink::WebString uuid; + if (web_blob_registry_->GetUUIDForURL(url_, &uuid)) { + blink::WebVector<blink::WebBlobData::Item*> items; + if (web_blob_registry_->GetBlobItems(uuid, &items)) { + // The blob data exists in our service, and we don't want to create a + // data pipe just to do a funny dance where at the end, we stuff data + // from memory into data pipes so we can read back the data. + OnReceiveWebBlobData(request, items); + return; + } + } + + url_loader_->Start(url_request.Pass(), + base::Bind(&WebURLLoaderImpl::OnReceivedResponse, + weak_factory_.GetWeakPtr(), request)); } void WebURLLoaderImpl::cancel() { @@ -187,6 +208,9 @@ new_request.setSkipServiceWorker(request.skipServiceWorker()); new_request.setFetchRequestMode(request.fetchRequestMode()); new_request.setFetchCredentialsMode(request.fetchCredentialsMode()); + new_request.setHTTPReferrer( + WebString::fromUTF8(url_response->redirect_referrer), + referrer_policy_); std::string old_method = request.httpMethod().utf8(); new_request.setHTTPMethod( @@ -208,6 +232,34 @@ request)); } +void WebURLLoaderImpl::OnReceiveWebBlobData( + const blink::WebURLRequest& request, + const blink::WebVector<blink::WebBlobData::Item*>& items) { + blink::WebURLResponse result; + result.initialize(); + result.setURL(url_); + result.setHTTPStatusCode(200); + result.setExpectedContentLength(-1); // Not available. + + base::WeakPtr<WebURLLoaderImpl> self(weak_factory_.GetWeakPtr()); + client_->didReceiveResponse(this, result); + + // We may have been deleted during didReceiveResponse. + if (!self) + return; + + // Send a receive data for each blob item. + for (size_t i = 0; i < items.size(); ++i) { + client_->didReceiveData(this, items[i]->data.data(), items[i]->data.size(), + -1); + } + + // Send a closing finish. + double finish_time = base::Time::Now().ToDoubleT(); + client_->didFinishLoading( + this, finish_time, blink::WebURLLoaderClient::kUnknownEncodedDataLength); +} + void WebURLLoaderImpl::ReadMore() { const void* buf; uint32_t buf_size;
diff --git a/mojo/services/html_viewer/weburlloader_impl.h b/components/html_viewer/web_url_loader_impl.h similarity index 76% rename from mojo/services/html_viewer/weburlloader_impl.h rename to components/html_viewer/web_url_loader_impl.h index 10f4040..4a92a8aa 100644 --- a/mojo/services/html_viewer/weburlloader_impl.h +++ b/components/html_viewer/web_url_loader_impl.h
@@ -2,13 +2,16 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef MOJO_SERVICES_HTML_VIEWER_WEBURLLOADER_IMPL_H_ -#define MOJO_SERVICES_HTML_VIEWER_WEBURLLOADER_IMPL_H_ +#ifndef COMPONENTS_HTML_VIEWER_WEB_URL_LOADER_IMPL_H_ +#define COMPONENTS_HTML_VIEWER_WEB_URL_LOADER_IMPL_H_ #include "base/macros.h" #include "base/memory/weak_ptr.h" +#include "components/html_viewer/mock_web_blob_registry_impl.h" #include "mojo/common/handle_watcher.h" #include "mojo/services/network/public/interfaces/url_loader.mojom.h" +#include "third_party/WebKit/public/platform/WebBlobData.h" +#include "third_party/WebKit/public/platform/WebReferrerPolicy.h" #include "third_party/WebKit/public/platform/WebURLLoader.h" #include "third_party/WebKit/public/platform/WebURLRequest.h" @@ -29,7 +32,8 @@ class WebURLLoaderImpl : public blink::WebURLLoader { public: - explicit WebURLLoaderImpl(mojo::NetworkService* network_service); + explicit WebURLLoaderImpl(mojo::NetworkService* network_service, + MockWebBlobRegistryImpl* web_blob_registry); private: virtual ~WebURLLoaderImpl(); @@ -49,12 +53,17 @@ void OnReceivedError(mojo::URLResponsePtr response); void OnReceivedRedirect(const blink::WebURLRequest& request, mojo::URLResponsePtr response); + void OnReceiveWebBlobData( + const blink::WebURLRequest& request, + const blink::WebVector<blink::WebBlobData::Item*>& items); void ReadMore(); void WaitToReadMore(); void OnResponseBodyStreamReady(MojoResult result); blink::WebURLLoaderClient* client_; + MockWebBlobRegistryImpl* web_blob_registry_; GURL url_; + blink::WebReferrerPolicy referrer_policy_; mojo::URLLoaderPtr url_loader_; mojo::ScopedDataPipeConsumerHandle response_body_stream_; mojo::common::HandleWatcher handle_watcher_; @@ -66,4 +75,4 @@ } // namespace html_viewer -#endif // MOJO_SERVICES_HTML_VIEWER_WEBURLLOADER_IMPL_H_ +#endif // COMPONENTS_HTML_VIEWER_WEB_URL_LOADER_IMPL_H_
diff --git a/components/metrics/proto/system_profile.proto b/components/metrics/proto/system_profile.proto index 85340ad..ca277cb 100644 --- a/components/metrics/proto/system_profile.proto +++ b/components/metrics/proto/system_profile.proto
@@ -77,7 +77,12 @@ // Information on the user's operating system. message OS { - // The user's operating system. + // The user's operating system. This should be one of: + // - Android + // - Windows NT + // - Linux (includes ChromeOS) + // - iPhone OS + // - Mac OS X optional string name = 1; // The version of the OS. The meaning of this field is OS-dependent.
diff --git a/components/nacl/browser/nacl_browser.cc b/components/nacl/browser/nacl_browser.cc index f53b3e0..733e9d9 100644 --- a/components/nacl/browser/nacl_browser.cc +++ b/components/nacl/browser/nacl_browser.cc
@@ -168,7 +168,7 @@ } void NaClBrowser::EarlyStartup() { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); InitIrtFilePath(); InitValidationCacheFilePath(); } @@ -225,21 +225,21 @@ } const base::File& NaClBrowser::IrtFile() const { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); CHECK_EQ(irt_state_, NaClResourceReady); CHECK(irt_file_.IsValid()); return irt_file_; } void NaClBrowser::EnsureAllResourcesAvailable() { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); EnsureIrtAvailable(); EnsureValidationCacheAvailable(); } // Load the IRT async. void NaClBrowser::EnsureIrtAvailable() { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); if (IsOk() && irt_state_ == NaClResourceUninitialized) { irt_state_ = NaClResourceRequested; // TODO(ncbray) use blocking pool. @@ -260,7 +260,7 @@ void NaClBrowser::OnIrtOpened(scoped_ptr<base::FileProxy> file_proxy, base::File::Error error_code) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); DCHECK_EQ(irt_state_, NaClResourceRequested); if (file_proxy->IsValid()) { irt_file_ = file_proxy->TakeFile(); @@ -322,7 +322,7 @@ } void NaClBrowser::EnsureValidationCacheAvailable() { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); if (IsOk() && validation_cache_state_ == NaClResourceUninitialized) { if (ValidationCacheIsEnabled()) { validation_cache_state_ = NaClResourceRequested; @@ -347,7 +347,7 @@ } void NaClBrowser::OnValidationCacheLoaded(const std::string *data) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); // Did the cache get cleared before the load completed? If so, ignore the // incoming data. if (validation_cache_state_ == NaClResourceReady) @@ -373,7 +373,7 @@ } void NaClBrowser::CheckWaiting() { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); if (!IsOk() || IsReady()) { // Queue the waiting tasks into the message loop. This helps avoid // re-entrancy problems that could occur if the closure was invoked @@ -404,7 +404,7 @@ void NaClBrowser::PutFilePath(const base::FilePath& path, uint64* file_token_lo, uint64* file_token_hi) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); while (true) { uint64 file_token[2] = {base::RandUint64(), base::RandUint64()}; // A zero file_token indicates there is no file_token, if we get zero, ask @@ -425,7 +425,7 @@ bool NaClBrowser::GetFilePath(uint64 file_token_lo, uint64 file_token_hi, base::FilePath* path) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); uint64 file_token[2] = {file_token_lo, file_token_hi}; std::string key(reinterpret_cast<char*>(file_token), sizeof(file_token)); PathCacheType::iterator iter = path_cache_.Peek(key); @@ -441,7 +441,7 @@ bool NaClBrowser::QueryKnownToValidate(const std::string& signature, bool off_the_record) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); if (off_the_record) { // If we're off the record, don't reorder the main cache. return validation_cache_.QueryKnownToValidate(signature, false) || @@ -457,7 +457,7 @@ void NaClBrowser::SetKnownToValidate(const std::string& signature, bool off_the_record) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); if (off_the_record) { off_the_record_validation_cache_.SetKnownToValidate(signature); } else { @@ -470,7 +470,7 @@ } void NaClBrowser::ClearValidationCache(const base::Closure& callback) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); // Note: this method may be called before EnsureValidationCacheAvailable has // been invoked. In other words, this method may be called before any NaCl // processes have been created. This method must succeed and invoke the @@ -522,7 +522,7 @@ } void NaClBrowser::PersistValidationCache() { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); // validation_cache_is_modified_ may be false if the cache was cleared while // this delayed task was pending. // validation_cache_file_path_ may be empty if something went wrong during @@ -549,7 +549,7 @@ } void NaClBrowser::OnProcessCrashed() { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); if (crash_times_.size() == kMaxCrashesPerInterval) { crash_times_.pop_front(); } @@ -558,7 +558,7 @@ } bool NaClBrowser::IsThrottled() { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); if (crash_times_.size() != kMaxCrashesPerInterval) { return false; }
diff --git a/components/nacl/common/DEPS b/components/nacl/common/DEPS index e13224d..3b9fcea 100644 --- a/components/nacl/common/DEPS +++ b/components/nacl/common/DEPS
@@ -1,3 +1,4 @@ include_rules = [ "+native_client/src/public", + "+native_client/src/trusted/service_runtime/nacl_error_code.h", ]
diff --git a/components/nacl/common/nacl_renderer_messages.h b/components/nacl/common/nacl_renderer_messages.h index e07a082..265af86 100644 --- a/components/nacl/common/nacl_renderer_messages.h +++ b/components/nacl/common/nacl_renderer_messages.h
@@ -6,6 +6,7 @@ // Multiply-included message file, no traditional include guard. #include "ipc/ipc_message_macros.h" +#include "native_client/src/trusted/service_runtime/nacl_error_code.h" #define IPC_MESSAGE_START NaClHostMsgStart @@ -13,3 +14,10 @@ // NaCl to the renderer before the NaCl process exits very soon after. IPC_SYNC_MESSAGE_CONTROL1_0(NaClRendererMsg_ReportExitStatus, int /* exit_status */) + +IPC_ENUM_TRAITS_MAX_VALUE(NaClErrorCode, NACL_ERROR_CODE_MAX) + +// This message must be synchronous to ensure that the load status is sent from +// NaCl to the renderer before the NaCl process exits very soon after. +IPC_SYNC_MESSAGE_CONTROL1_0(NaClRendererMsg_ReportLoadStatus, + NaClErrorCode /* load_status */)
diff --git a/components/nacl/loader/nacl_listener.cc b/components/nacl/loader/nacl_listener.cc index 4a58a1c..4cf4a3e 100644 --- a/components/nacl/loader/nacl_listener.cc +++ b/components/nacl/loader/nacl_listener.cc
@@ -69,6 +69,12 @@ copy_bytes); } +void LoadStatusCallback(int load_status) { + g_listener->trusted_listener()->Send( + new NaClRendererMsg_ReportLoadStatus( + static_cast<NaClErrorCode>(load_status))); +} + #if defined(OS_MACOSX) // On Mac OS X, shm_open() works in the sandbox but does not give us @@ -420,6 +426,7 @@ args->debug_stub_server_port_selected_handler_func = DebugStubPortSelectedHandler; #endif + args->load_status_handler_func = LoadStatusCallback; #if defined(OS_LINUX) args->prereserved_sandbox_size = prereserved_sandbox_size_; #endif
diff --git a/components/nacl/loader/nacl_listener.h b/components/nacl/loader/nacl_listener.h index 9e8d67b6..eccbc19 100644 --- a/components/nacl/loader/nacl_listener.h +++ b/components/nacl/loader/nacl_listener.h
@@ -48,6 +48,10 @@ void* crash_info_shmem_memory() const { return crash_info_shmem_->memory(); } + NaClTrustedListener* trusted_listener() const { + return trusted_listener_.get(); + } + typedef base::Callback<void(IPC::PlatformFileForTransit, base::FilePath)> ResolveFileTokenCallback; void ResolveFileToken(uint64_t token_lo,
diff --git a/components/nacl/renderer/DEPS b/components/nacl/renderer/DEPS index 889a36b7..483376be 100644 --- a/components/nacl/renderer/DEPS +++ b/components/nacl/renderer/DEPS
@@ -1,6 +1,8 @@ include_rules = [ "+content/public/renderer", "+native_client/src/public/imc_types.h", # for NaClHandle + # for NaClErrorCode + "+native_client/src/trusted/service_runtime/nacl_error_code.h", "+net", "+ppapi/c", "+ppapi/proxy",
diff --git a/components/nacl/renderer/plugin/service_runtime.cc b/components/nacl/renderer/plugin/service_runtime.cc index 485792a..d697c69 100644 --- a/components/nacl/renderer/plugin/service_runtime.cc +++ b/components/nacl/renderer/plugin/service_runtime.cc
@@ -88,6 +88,10 @@ // the plugin. load_status = LOAD_OK; } else { + // We invoke start_module to unblock NaClWaitForStartModuleCommand in + // sel_main_chrome.c on the NaCl side, but the load_status is obtained by + // a different hook. Remove this once NaClWaitForStartModuleCommand is no + // longer needed. NaClSrpcResultCodes rpc_result = NaClSrpcInvokeBySignature(&command_channel_, "start_module::i", @@ -103,23 +107,7 @@ } NaClLog(4, "ServiceRuntime::StartModule (load_status=%d)\n", load_status); - if (main_service_runtime_) { - if (load_status < 0 || load_status > NACL_ERROR_CODE_MAX) - load_status = LOAD_STATUS_UNKNOWN; - GetNaClInterface()->ReportSelLdrStatus(pp_instance_, - load_status, - NACL_ERROR_CODE_MAX); - } - - if (LOAD_OK != load_status) { - ErrorInfo error_info; - error_info.SetReport( - PP_NACL_ERROR_SEL_LDR_START_STATUS, - NaClErrorString(static_cast<NaClErrorCode>(load_status))); - ReportLoadError(error_info); - return false; - } - return true; + return LOAD_OK == load_status; } void ServiceRuntime::StartSelLdr(const SelLdrStartParams& params, @@ -208,8 +196,6 @@ bool ok = StartNexeInternal(); if (ok) { NaClLog(4, "ServiceRuntime::StartNexe (success)\n"); - } else { - ReapLogs(); } // This only matters if a background thread is waiting, but we signal in all // cases to simplify the code. @@ -222,24 +208,6 @@ return StartModule(); } -void ServiceRuntime::ReapLogs() { - // TODO(teravest): We should allow the NaCl process to crash itself when a - // module fails to start, and remove the call to RemoteLog() here. The - // reverse channel is no longer needed for crash reporting. - // - // The reasoning behind the current code behavior follows: - // On a load failure the NaCl process does not crash itself to - // avoid a race where the no-more-senders error on the reverse - // channel service thread might cause the crash-detection logic to - // kick in before the start_module RPC reply has been received. So - // we induce a NaCl process crash here. - RemoteLog(LOG_FATAL, "reap logs\n"); - - // TODO(teravest): Release subprocess_ here since it's no longer needed. It - // was previously kept around to collect crash log output from the bootstrap - // channel. -} - void ServiceRuntime::ReportLoadError(const ErrorInfo& error_info) { if (main_service_runtime_) { plugin_->ReportLoadError(error_info); @@ -264,15 +232,6 @@ } } -bool ServiceRuntime::RemoteLog(int severity, const std::string& msg) { - NaClSrpcResultCodes rpc_result = - NaClSrpcInvokeBySignature(&command_channel_, - "log:is:", - severity, - strdup(msg.c_str())); - return (NACL_SRPC_RESULT_OK == rpc_result); -} - void ServiceRuntime::Shutdown() { // Abandon callbacks, tell service threads to quit if they were // blocked waiting for main thread operations to finish. Note that
diff --git a/components/nacl/renderer/plugin/service_runtime.h b/components/nacl/renderer/plugin/service_runtime.h index 5fe941a1..f1991b62 100644 --- a/components/nacl/renderer/plugin/service_runtime.h +++ b/components/nacl/renderer/plugin/service_runtime.h
@@ -85,7 +85,6 @@ // Starts the application channel to the nexe. SrpcClient* SetupAppChannel(); - bool RemoteLog(int severity, const std::string& msg); Plugin* plugin() const { return plugin_; } void Shutdown(); @@ -97,7 +96,6 @@ bool SetupCommandChannel(); bool StartModule(); - void ReapLogs(); void ReportLoadError(const ErrorInfo& error_info);
diff --git a/components/nacl/renderer/ppb_nacl_private.h b/components/nacl/renderer/ppb_nacl_private.h index 7953461b..2683652 100644 --- a/components/nacl/renderer/ppb_nacl_private.h +++ b/components/nacl/renderer/ppb_nacl_private.h
@@ -326,13 +326,6 @@ const char* url, struct PP_NaClFileInfo* file_info, struct PP_CompletionCallback callback); - /* Reports the status of sel_ldr for UMA reporting. - * |max_status| has to be provided because the implementation of this - * interface can't access the NaClErrorCode enum. - */ - void (*ReportSelLdrStatus)(PP_Instance instance, - int32_t load_status, - int32_t max_status); /* Logs time taken by an operation to UMA histograms. * This function is safe to call on any thread. */ @@ -365,4 +358,3 @@ */ #endif /* COMPONENTS_NACL_RENDERER_PPB_NACL_PRIVATE_H_ */ -
diff --git a/components/nacl/renderer/ppb_nacl_private_impl.cc b/components/nacl/renderer/ppb_nacl_private_impl.cc index c879e48..2f3cd58 100644 --- a/components/nacl/renderer/ppb_nacl_private_impl.cc +++ b/components/nacl/renderer/ppb_nacl_private_impl.cc
@@ -14,6 +14,7 @@ #include "base/containers/scoped_ptr_hash_map.h" #include "base/cpu.h" #include "base/files/file.h" +#include "base/json/json_reader.h" #include "base/lazy_instance.h" #include "base/logging.h" #include "base/rand_util.h" @@ -61,8 +62,6 @@ #include "third_party/WebKit/public/web/WebPluginContainer.h" #include "third_party/WebKit/public/web/WebSecurityOrigin.h" #include "third_party/WebKit/public/web/WebURLLoaderOptions.h" -#include "third_party/jsoncpp/source/include/json/reader.h" -#include "third_party/jsoncpp/source/include/json/value.h" namespace nacl { namespace { @@ -501,13 +500,13 @@ // Create the trusted plugin channel. if (IsValidChannelHandle(launch_result.trusted_ipc_channel_handle)) { - bool report_exit_status = PP_ToBool(main_service_runtime); + bool is_helper_nexe = !PP_ToBool(main_service_runtime); scoped_ptr<TrustedPluginChannel> trusted_plugin_channel( new TrustedPluginChannel( load_manager, launch_result.trusted_ipc_channel_handle, content::RenderThread::Get()->GetShutdownEvent(), - report_exit_status)); + is_helper_nexe)); load_manager->set_trusted_plugin_channel(trusted_plugin_channel.Pass()); } else { PostPPCompletionCallback(callback, PP_ERROR_FAILED); @@ -1179,43 +1178,41 @@ buffer.get()[rc] = 0; // Expect the JSON file to contain a top-level object (dictionary). - Json::Reader json_reader; - Json::Value json_data; - if (!json_reader.parse(buffer.get(), json_data)) { + base::JSONReader json_reader; + int json_read_error_code; + std::string json_read_error_msg; + base::Value* json_data = json_reader.ReadAndReturnError( + buffer.get(), + base::JSON_PARSE_RFC, + &json_read_error_code, + &json_read_error_msg); + if (json_data == NULL) { load_manager->ReportLoadError( PP_NACL_ERROR_PNACL_RESOURCE_FETCH, std::string("Parsing resource info failed: JSON parse error: ") + - json_reader.getFormattedErrorMessages()); + json_read_error_msg); return PP_FALSE; } - if (!json_data.isObject()) { + base::DictionaryValue* json_dict; + if (!json_data->GetAsDictionary(&json_dict)) { load_manager->ReportLoadError( PP_NACL_ERROR_PNACL_RESOURCE_FETCH, "Parsing resource info failed: Malformed JSON dictionary"); return PP_FALSE; } - if (json_data.isMember("pnacl-llc-name")) { - Json::Value json_name = json_data["pnacl-llc-name"]; - if (json_name.isString()) { - *llc_tool_name = ppapi::StringVar::StringToPPVar(json_name.asString()); - } - } + std::string pnacl_llc_name; + if (json_dict->GetString("pnacl-llc-name", &pnacl_llc_name)) + *llc_tool_name = ppapi::StringVar::StringToPPVar(pnacl_llc_name); - if (json_data.isMember("pnacl-ld-name")) { - Json::Value json_name = json_data["pnacl-ld-name"]; - if (json_name.isString()) { - *ld_tool_name = ppapi::StringVar::StringToPPVar(json_name.asString()); - } - } + std::string pnacl_ld_name; + if (json_dict->GetString("pnacl-ld-name", &pnacl_ld_name)) + *ld_tool_name = ppapi::StringVar::StringToPPVar(pnacl_ld_name); - if (json_data.isMember("pnacl-sz-name")) { - Json::Value json_name = json_data["pnacl-sz-name"]; - if (json_name.isString()) { - *subzero_tool_name = - ppapi::StringVar::StringToPPVar(json_name.asString()); - } + std::string pnacl_sz_name; + if (json_dict->GetString("pnacl-sz-name", &pnacl_sz_name)) { + *subzero_tool_name = ppapi::StringVar::StringToPPVar(pnacl_sz_name); } else { // TODO(jvoung): remove fallback after one chrome release // or when we bump the kMinPnaclVersion. @@ -1487,22 +1484,6 @@ file_downloader->Load(url_request); } -void ReportSelLdrStatus(PP_Instance instance, - int32_t load_status, - int32_t max_status) { - HistogramEnumerate("NaCl.LoadStatus.SelLdr", load_status, max_status); - NexeLoadManager* load_manager = GetNexeLoadManager(instance); - DCHECK(load_manager); - if (!load_manager) - return; - - // Gather data to see if being installed changes load outcomes. - const char* name = load_manager->is_installed() ? - "NaCl.LoadStatus.SelLdr.InstalledApp" : - "NaCl.LoadStatus.SelLdr.NotInstalledApp"; - HistogramEnumerate(name, load_status, max_status); -} - void LogTranslateTime(const char* histogram_name, int64_t time_in_us) { ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask( @@ -1720,7 +1701,6 @@ &GetPNaClResourceInfo, &GetCpuFeatureAttrs, &DownloadNexe, - &ReportSelLdrStatus, &LogTranslateTime, &LogBytesCompiledVsDowloaded, &SetPNaClStartTime,
diff --git a/components/nacl/renderer/trusted_plugin_channel.cc b/components/nacl/renderer/trusted_plugin_channel.cc index 729644cb..1ba39fe 100644 --- a/components/nacl/renderer/trusted_plugin_channel.cc +++ b/components/nacl/renderer/trusted_plugin_channel.cc
@@ -6,6 +6,7 @@ #include "base/callback_helpers.h" #include "components/nacl/common/nacl_renderer_messages.h" +#include "components/nacl/renderer/histogram.h" #include "components/nacl/renderer/nexe_load_manager.h" #include "content/public/renderer/render_thread.h" #include "ipc/ipc_sync_channel.h" @@ -18,9 +19,9 @@ NexeLoadManager* nexe_load_manager, const IPC::ChannelHandle& handle, base::WaitableEvent* shutdown_event, - bool report_exit_status) + bool is_helper_nexe) : nexe_load_manager_(nexe_load_manager), - report_exit_status_(report_exit_status) { + is_helper_nexe_(is_helper_nexe) { channel_ = IPC::SyncChannel::Create( handle, IPC::Channel::MODE_CLIENT, @@ -41,19 +42,41 @@ bool handled = true; IPC_BEGIN_MESSAGE_MAP(TrustedPluginChannel, msg) IPC_MESSAGE_HANDLER(NaClRendererMsg_ReportExitStatus, OnReportExitStatus); + IPC_MESSAGE_HANDLER(NaClRendererMsg_ReportLoadStatus, OnReportLoadStatus); IPC_MESSAGE_UNHANDLED(handled = false); IPC_END_MESSAGE_MAP() return handled; } void TrustedPluginChannel::OnChannelError() { - if (report_exit_status_) + if (!is_helper_nexe_) nexe_load_manager_->NexeDidCrash(); } void TrustedPluginChannel::OnReportExitStatus(int exit_status) { - if (report_exit_status_) + if (!is_helper_nexe_) nexe_load_manager_->set_exit_status(exit_status); } +void TrustedPluginChannel::OnReportLoadStatus(NaClErrorCode load_status) { + if (load_status < 0 || load_status > NACL_ERROR_CODE_MAX) { + load_status = LOAD_STATUS_UNKNOWN; + } + // For now, we only report UMA for non-helper nexes + // (don't report for the PNaCl translators nexes). + if (!is_helper_nexe_) { + HistogramEnumerate("NaCl.LoadStatus.SelLdr", load_status, + NACL_ERROR_CODE_MAX); + // Gather data to see if being installed changes load outcomes. + const char* name = nexe_load_manager_->is_installed() + ? "NaCl.LoadStatus.SelLdr.InstalledApp" + : "NaCl.LoadStatus.SelLdr.NotInstalledApp"; + HistogramEnumerate(name, load_status, NACL_ERROR_CODE_MAX); + } + if (load_status != LOAD_OK) { + nexe_load_manager_->ReportLoadError(PP_NACL_ERROR_SEL_LDR_START_STATUS, + NaClErrorString(load_status)); + } +} + } // namespace nacl
diff --git a/components/nacl/renderer/trusted_plugin_channel.h b/components/nacl/renderer/trusted_plugin_channel.h index efeb5e8..d657d53 100644 --- a/components/nacl/renderer/trusted_plugin_channel.h +++ b/components/nacl/renderer/trusted_plugin_channel.h
@@ -8,6 +8,7 @@ #include "base/callback.h" #include "base/memory/scoped_ptr.h" #include "ipc/ipc_listener.h" +#include "native_client/src/trusted/service_runtime/nacl_error_code.h" #include "ppapi/c/pp_instance.h" namespace base { @@ -28,7 +29,7 @@ TrustedPluginChannel(NexeLoadManager* nexe_load_manager, const IPC::ChannelHandle& handle, base::WaitableEvent* shutdown_event, - bool report_exit_status); + bool is_helper_nexe); ~TrustedPluginChannel() override; bool Send(IPC::Message* message); @@ -38,13 +39,14 @@ void OnChannelError() override; void OnReportExitStatus(int exit_status); + void OnReportLoadStatus(NaClErrorCode load_status); private: // Non-owning pointer. This is safe because the TrustedPluginChannel is owned // by the NexeLoadManager pointed to here. NexeLoadManager* nexe_load_manager_; scoped_ptr<IPC::SyncChannel> channel_; - bool report_exit_status_; + bool is_helper_nexe_; DISALLOW_COPY_AND_ASSIGN(TrustedPluginChannel); };
diff --git a/components/nacl_nonsfi.gyp b/components/nacl_nonsfi.gyp index 867bce0..3df9c453 100644 --- a/components/nacl_nonsfi.gyp +++ b/components/nacl_nonsfi.gyp
@@ -140,7 +140,6 @@ '../native_client/src/nonsfi/irt/irt.gyp:nacl_sys_private', '../native_client/src/nonsfi/loader/loader.gyp:elf_loader', '../native_client/src/untrusted/nacl/nacl.gyp:nacl_lib_newlib', - '../native_client/tools.gyp:prep_toolchain', '../ppapi/ppapi_proxy_nacl.gyp:ppapi_proxy_nacl', '../sandbox/sandbox_nacl_nonsfi.gyp:sandbox_nacl_nonsfi', ],
diff --git a/components/navigation_interception/intercept_navigation_resource_throttle_unittest.cc b/components/navigation_interception/intercept_navigation_resource_throttle_unittest.cc index 3640e8f..8855f42 100644 --- a/components/navigation_interception/intercept_navigation_resource_throttle_unittest.cc +++ b/components/navigation_interception/intercept_navigation_resource_throttle_unittest.cc
@@ -119,7 +119,7 @@ url, net::DEFAULT_PRIORITY, NULL /* delegate */)) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); if (render_process_id != MSG_ROUTING_NONE && render_frame_id != MSG_ROUTING_NONE) { content::ResourceRequestInfo::AllocateForTesting( @@ -150,13 +150,13 @@ } void ThrottleWillStartRequest(bool* defer) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); throttle_->WillStartRequest(defer); } void ThrottleWillRedirectRequest(const net::RedirectInfo& redirect_info, bool* defer) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); throttle_->WillRedirectRequest(redirect_info, defer); } @@ -211,7 +211,7 @@ int render_process_id, int render_frame_id, bool* defer) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); TestIOThreadState* io_thread_state = new TestIOThreadState(url, render_process_id, render_frame_id, request_method, redirect_mode,
diff --git a/components/omnibox/autocomplete_match.h b/components/omnibox/autocomplete_match.h index 93c5097..8fd50ac 100644 --- a/components/omnibox/autocomplete_match.h +++ b/components/omnibox/autocomplete_match.h
@@ -340,6 +340,11 @@ // Set with a keyword provider match if this match can show a keyword hint. // For example, if this is a SearchProvider match for "www.amazon.com", // |associated_keyword| could be a KeywordProvider match for "amazon.com". + // + // When this is set, the popup will show a ">" symbol at the right edge of the + // line for this match, and tab/shift-tab will toggle in and out of keyword + // mode without disturbing the rest of the popup. See also + // OmniboxPopupModel::SetSelectedLineState(). scoped_ptr<AutocompleteMatch> associated_keyword; // The keyword of the TemplateURL the match originated from. This is nonempty
diff --git a/components/pairing/bluetooth_controller_pairing_controller.cc b/components/pairing/bluetooth_controller_pairing_controller.cc index 1d8e026..476b205 100644 --- a/components/pairing/bluetooth_controller_pairing_controller.cc +++ b/components/pairing/bluetooth_controller_pairing_controller.cc
@@ -417,6 +417,11 @@ ChangeStage(STAGE_HOST_ENROLLMENT_ERROR); } +void BluetoothControllerPairingController::OnAddNetworkMessage( + const pairing_api::AddNetwork& message) { + NOTREACHED(); +} + void BluetoothControllerPairingController::DeviceAdded( device::BluetoothAdapter* adapter, device::BluetoothDevice* device) {
diff --git a/components/pairing/bluetooth_controller_pairing_controller.h b/components/pairing/bluetooth_controller_pairing_controller.h index c4a3258d..ab67aed 100644 --- a/components/pairing/bluetooth_controller_pairing_controller.h +++ b/components/pairing/bluetooth_controller_pairing_controller.h
@@ -83,6 +83,7 @@ void OnCompleteSetupMessage( const pairing_api::CompleteSetup& message) override; void OnErrorMessage(const pairing_api::Error& message) override; + void OnAddNetworkMessage(const pairing_api::AddNetwork& message) override; // BluetoothAdapter::Observer: void DeviceAdded(device::BluetoothAdapter* adapter,
diff --git a/components/pairing/bluetooth_host_pairing_controller.cc b/components/pairing/bluetooth_host_pairing_controller.cc index 5ffd2f5..580d92f0 100644 --- a/components/pairing/bluetooth_host_pairing_controller.cc +++ b/components/pairing/bluetooth_host_pairing_controller.cc
@@ -307,11 +307,12 @@ void BluetoothHostPairingController::OnConfigureHostMessage( const pairing_api::ConfigureHost& message) { FOR_EACH_OBSERVER(Observer, observers_, - ConfigureHost(message.parameters().accepted_eula(), - message.parameters().lang(), - message.parameters().timezone(), - message.parameters().send_reports(), - message.parameters().keyboard_layout())); + ConfigureHostRequested( + message.parameters().accepted_eula(), + message.parameters().lang(), + message.parameters().timezone(), + message.parameters().send_reports(), + message.parameters().keyboard_layout())); } void BluetoothHostPairingController::OnPairDevicesMessage( @@ -319,7 +320,8 @@ DCHECK(thread_checker_.CalledOnValidThread()); ChangeStage(STAGE_ENROLLING); FOR_EACH_OBSERVER(Observer, observers_, - EnrollHost(message.parameters().admin_access_token())); + EnrollHostRequested( + message.parameters().admin_access_token())); } void BluetoothHostPairingController::OnCompleteSetupMessage( @@ -339,6 +341,13 @@ NOTREACHED(); } +void BluetoothHostPairingController::OnAddNetworkMessage( + const pairing_api::AddNetwork& message) { + DCHECK(thread_checker_.CalledOnValidThread()); + FOR_EACH_OBSERVER(Observer, observers_, + AddNetworkRequested(message.parameters().onc_spec())); +} + void BluetoothHostPairingController::AdapterPresentChanged( device::BluetoothAdapter* adapter, bool present) {
diff --git a/components/pairing/bluetooth_host_pairing_controller.h b/components/pairing/bluetooth_host_pairing_controller.h index b99af24d..20ba5066 100644 --- a/components/pairing/bluetooth_host_pairing_controller.h +++ b/components/pairing/bluetooth_host_pairing_controller.h
@@ -80,6 +80,7 @@ void OnCompleteSetupMessage( const pairing_api::CompleteSetup& message) override; void OnErrorMessage(const pairing_api::Error& message) override; + void OnAddNetworkMessage(const pairing_api::AddNetwork& message) override; // BluetoothAdapter::Observer: void AdapterPresentChanged(device::BluetoothAdapter* adapter,
diff --git a/components/pairing/fake_host_pairing_controller.cc b/components/pairing/fake_host_pairing_controller.cc index e6d1e76..25250d02 100644 --- a/components/pairing/fake_host_pairing_controller.cc +++ b/components/pairing/fake_host_pairing_controller.cc
@@ -169,15 +169,4 @@ } } -void FakeHostPairingController::ConfigureHost( - bool accepted_eula, - const std::string& lang, - const std::string& timezone, - bool send_reports, - const std::string& keyboard_layout) { -} - -void FakeHostPairingController::EnrollHost(const std::string& auth_token) { -} - } // namespace pairing_chromeos
diff --git a/components/pairing/fake_host_pairing_controller.h b/components/pairing/fake_host_pairing_controller.h index 8ef2f4b..17d68840 100644 --- a/components/pairing/fake_host_pairing_controller.h +++ b/components/pairing/fake_host_pairing_controller.h
@@ -50,12 +50,6 @@ // HostPairingController::Observer: void PairingStageChanged(Stage new_stage) override; - void ConfigureHost(bool accepted_eula, - const std::string& lang, - const std::string& timezone, - bool send_reports, - const std::string& keyboard_layout) override; - void EnrollHost(const std::string& auth_token) override; ObserverList<Observer> observers_; Stage current_stage_;
diff --git a/components/pairing/host_pairing_controller.h b/components/pairing/host_pairing_controller.h index 7d79c4f..731707a 100644 --- a/components/pairing/host_pairing_controller.h +++ b/components/pairing/host_pairing_controller.h
@@ -49,14 +49,17 @@ virtual void PairingStageChanged(Stage new_stage) = 0; // Called when the controller has sent a configuration to apply. - virtual void ConfigureHost(bool accepted_eula, - const std::string& lang, - const std::string& timezone, - bool send_reports, - const std::string& keyboard_layout) = 0; + virtual void ConfigureHostRequested(bool accepted_eula, + const std::string& lang, + const std::string& timezone, + bool send_reports, + const std::string& keyboard_layout) {} + + // Called when the controller has sent a network to add. + virtual void AddNetworkRequested(const std::string& onc_spec) {} // Called when the controller has provided an |auth_token| for enrollment. - virtual void EnrollHost(const std::string& auth_token) = 0; + virtual void EnrollHostRequested(const std::string& auth_token) {} private: DISALLOW_COPY_AND_ASSIGN(Observer);
diff --git a/components/pairing/pairing_api.proto b/components/pairing/pairing_api.proto index 0d8e6680..856feae6 100644 --- a/components/pairing/pairing_api.proto +++ b/components/pairing/pairing_api.proto
@@ -86,3 +86,12 @@ optional int32 api_version = 1; optional ErrorParameters parameters = 2; } + +message AddNetworkParameters { + optional string onc_spec = 1; +} + +message AddNetwork { + optional int32 api_version = 1; + optional AddNetworkParameters parameters = 2; +}
diff --git a/components/pairing/proto_decoder.cc b/components/pairing/proto_decoder.cc index 96b3c24f..f8e42a4b 100644 --- a/components/pairing/proto_decoder.cc +++ b/components/pairing/proto_decoder.cc
@@ -15,6 +15,7 @@ MESSAGE_PAIR_DEVICES, MESSAGE_COMPLETE_SETUP, MESSAGE_ERROR, + MESSAGE_ADD_NETWORK, NUM_MESSAGES, }; } @@ -105,9 +106,15 @@ observer_->OnErrorMessage(message); } break; + case MESSAGE_ADD_NETWORK: { + pairing_api::AddNetwork message; + message.ParseFromArray(&buffer[0], buffer.size()); + observer_->OnAddNetworkMessage(message); + } + break; default: - NOTREACHED(); + LOG(WARNING) << "Skipping unknown message type: " << next_message_type_; break; }
diff --git a/components/pairing/proto_decoder.h b/components/pairing/proto_decoder.h index e36eb7f..826c4bb0 100644 --- a/components/pairing/proto_decoder.h +++ b/components/pairing/proto_decoder.h
@@ -18,6 +18,7 @@ } namespace pairing_api { +class AddNetwork; class CompleteSetup; class ConfigureHost; class Error; @@ -47,6 +48,8 @@ const pairing_api::CompleteSetup& message) = 0; virtual void OnErrorMessage( const pairing_api::Error& message) = 0; + virtual void OnAddNetworkMessage( + const pairing_api::AddNetwork& message) = 0; protected: Observer() {}
diff --git a/components/pairing/shark_connection_listener.cc b/components/pairing/shark_connection_listener.cc index ce63aa0..a6bc655f 100644 --- a/components/pairing/shark_connection_listener.cc +++ b/components/pairing/shark_connection_listener.cc
@@ -30,17 +30,4 @@ } } -void SharkConnectionListener::ConfigureHost( - bool accepted_eula, - const std::string& lang, - const std::string& timezone, - bool send_reports, - const std::string& keyboard_layout) { - NOTREACHED(); -} - -void SharkConnectionListener::EnrollHost(const std::string& auth_token) { - NOTREACHED(); -} - } // namespace pairing_chromeos
diff --git a/components/pairing/shark_connection_listener.h b/components/pairing/shark_connection_listener.h index 98a7829..c0b3836b 100644 --- a/components/pairing/shark_connection_listener.h +++ b/components/pairing/shark_connection_listener.h
@@ -30,12 +30,6 @@ // HostPairingController::Observer overrides: void PairingStageChanged(Stage new_stage) override; - void ConfigureHost(bool accepted_eula, - const std::string& lang, - const std::string& timezone, - bool send_reports, - const std::string& keyboard_layout) override; - void EnrollHost(const std::string& auth_token) override; OnConnectedCallback callback_; scoped_ptr<HostPairingController> controller_;
diff --git a/components/password_manager/core/browser/affiliated_match_helper.cc b/components/password_manager/core/browser/affiliated_match_helper.cc index b89d44e..6acfb59f 100644 --- a/components/password_manager/core/browser/affiliated_match_helper.cc +++ b/components/password_manager/core/browser/affiliated_match_helper.cc
@@ -6,6 +6,8 @@ #include "base/bind.h" #include "base/callback.h" +#include "base/command_line.h" +#include "base/metrics/histogram_macros.h" #include "base/single_thread_task_runner.h" #include "base/thread_task_runner_handle.h" #include "components/autofill/core/common/password_form.h" @@ -15,6 +17,34 @@ namespace { +// Dummy Android facet URIs for which affiliations will be fetched as part of an +// experiment to exercise the AffiliationService code in the wild, before users +// would get a chance to have real Android credentials saved. +// Note: although somewhat redundant, the URLs are listed explicitly so that +// they are easy to find in code search if someone wonders why they are fetched. +const char* kDummyAndroidFacetURIs[] = { + "android://oEOFeXmqYvBlkpl3gJlItdIzb59KFnmFGuc1eHFQcIKpEWQuV2X4L7GYkRtdqTi_" + "g9YvgKFAXew3rMDjeAkWVA==@com.example.one", + "android://oEOFeXmqYvBlkpl3gJlItdIzb59KFnmFGuc1eHFQcIKpEWQuV2X4L7GYkRtdqTi_" + "g9YvgKFAXew3rMDjeAkWVA==@com.example.two", + "android://oEOFeXmqYvBlkpl3gJlItdIzb59KFnmFGuc1eHFQcIKpEWQuV2X4L7GYkRtdqTi_" + "g9YvgKFAXew3rMDjeAkWVA==@com.example.twoprime", + "android://oEOFeXmqYvBlkpl3gJlItdIzb59KFnmFGuc1eHFQcIKpEWQuV2X4L7GYkRtdqTi_" + "g9YvgKFAXew3rMDjeAkWVA==@com.example.three", + "android://oEOFeXmqYvBlkpl3gJlItdIzb59KFnmFGuc1eHFQcIKpEWQuV2X4L7GYkRtdqTi_" + "g9YvgKFAXew3rMDjeAkWVA==@com.example.four", + "android://oEOFeXmqYvBlkpl3gJlItdIzb59KFnmFGuc1eHFQcIKpEWQuV2X4L7GYkRtdqTi_" + "g9YvgKFAXew3rMDjeAkWVA==@com.example.fourprime"}; + +// Dummy Web facet URIs for the same purpose. The URIs with the same numbers are +// in the same equivalence class. +const char* kDummyWebFacetURIs[] = {"https://one.example.com", + "https://two.example.com", + "https://three.example.com", + "https://threeprime.example.com", + "https://four.example.com", + "https://fourprime.example.com"}; + // Returns whether or not |form| represents a credential for an Android // application, and if so, returns the |facet_uri| of that application. bool IsAndroidApplicationCredential(const autofill::PasswordForm& form, @@ -58,19 +88,47 @@ void AffiliatedMatchHelper::GetAffiliatedAndroidRealms( const autofill::PasswordForm& observed_form, const AffiliatedRealmsCallback& result_callback) { - FacetURI facet_uri( - FacetURI::FromPotentiallyInvalidSpec(observed_form.signon_realm)); - if (observed_form.scheme == autofill::PasswordForm::SCHEME_HTML && - observed_form.ssl_valid && facet_uri.IsValidWebFacetURI()) { + if (IsValidWebCredential(observed_form)) { + FacetURI facet_uri( + FacetURI::FromPotentiallyInvalidSpec(observed_form.signon_realm)); affiliation_service_->GetAffiliations( facet_uri, AffiliationService::StrategyOnCacheMiss::FAIL, - base::Bind(&AffiliatedMatchHelper::OnGetAffiliationsResults, + base::Bind(&AffiliatedMatchHelper::CompleteGetAffiliatedAndroidRealms, weak_ptr_factory_.GetWeakPtr(), facet_uri, result_callback)); } else { result_callback.Run(std::vector<std::string>()); } } +void AffiliatedMatchHelper::GetAffiliatedWebRealms( + const autofill::PasswordForm& android_form, + const AffiliatedRealmsCallback& result_callback) { + if (IsValidAndroidCredential(android_form)) { + affiliation_service_->GetAffiliations( + FacetURI::FromPotentiallyInvalidSpec(android_form.signon_realm), + AffiliationService::StrategyOnCacheMiss::FETCH_OVER_NETWORK, + base::Bind(&AffiliatedMatchHelper::CompleteGetAffiliatedWebRealms, + weak_ptr_factory_.GetWeakPtr(), result_callback)); + } else { + result_callback.Run(std::vector<std::string>()); + } +} + +// static +bool AffiliatedMatchHelper::IsValidAndroidCredential( + const autofill::PasswordForm& form) { + return form.scheme == autofill::PasswordForm::SCHEME_HTML && + IsValidAndroidFacetURI(form.signon_realm); +} + +// static +bool AffiliatedMatchHelper::IsValidWebCredential( + const autofill::PasswordForm& form) { + FacetURI facet_uri(FacetURI::FromPotentiallyInvalidSpec(form.signon_realm)); + return form.scheme == autofill::PasswordForm::SCHEME_HTML && form.ssl_valid && + facet_uri.IsValidWebFacetURI(); +} + // static ScopedVector<autofill::PasswordForm> AffiliatedMatchHelper::TransformAffiliatedAndroidCredentials( @@ -97,7 +155,7 @@ password_store_->GetAutofillableLogins(this); } -void AffiliatedMatchHelper::OnGetAffiliationsResults( +void AffiliatedMatchHelper::CompleteGetAffiliatedAndroidRealms( const FacetURI& original_facet_uri, const AffiliatedRealmsCallback& result_callback, const AffiliatedFacets& results, @@ -114,6 +172,44 @@ result_callback.Run(affiliated_realms); } +void AffiliatedMatchHelper::CompleteGetAffiliatedWebRealms( + const AffiliatedRealmsCallback& result_callback, + const AffiliatedFacets& results, + bool success) { + std::vector<std::string> affiliated_realms; + if (success) { + for (const FacetURI& affiliated_facet : results) { + if (affiliated_facet.IsValidWebFacetURI()) + // Facet URIs have no trailing slash, whereas realms do. + affiliated_realms.push_back(affiliated_facet.canonical_spec() + "/"); + } + } + result_callback.Run(affiliated_realms); +} + +void AffiliatedMatchHelper::VerifyAffiliationsForDummyFacets( + VerificationTiming timing) { + DCHECK(affiliation_service_); + for (const char* web_facet_uri : kDummyWebFacetURIs) { + // If affiliation for the Android facets has successfully been prefetched, + // then cache-restricted queries into affiliated Web facets should succeed. + affiliation_service_->GetAffiliations( + FacetURI::FromCanonicalSpec(web_facet_uri), + AffiliationService::StrategyOnCacheMiss::FAIL, + base::Bind(&OnRetrievedAffiliationResultsForDummyWebFacets, timing)); + } +} + +void AffiliatedMatchHelper::ScheduleVerifyAffiliationsForDummyFacets( + base::Timer* timer, + base::TimeDelta delay, + VerificationTiming timing) { + timer->Start( + FROM_HERE, delay, + base::Bind(&AffiliatedMatchHelper::VerifyAffiliationsForDummyFacets, + base::Unretained(this), timing)); +} + void AffiliatedMatchHelper::OnLoginsChanged( const PasswordStoreChangeList& changes) { for (const PasswordStoreChange& change : changes) { @@ -135,6 +231,51 @@ if (IsAndroidApplicationCredential(*form, &facet_uri)) affiliation_service_->Prefetch(facet_uri, base::Time::Max()); } + + // If the respective experiment is enabled, test prefetching affiliation data + // for dummy Android facet URIs to discover potenial issues in the wild, even + // before users would get a chance to have real Android credentials saved. + if (password_manager::IsAffiliationRequestsForDummyFacetsEnabled( + *base::CommandLine::ForCurrentProcess())) { + for (const char* android_facet_uri : kDummyAndroidFacetURIs) { + affiliation_service_->Prefetch( + FacetURI::FromCanonicalSpec(android_facet_uri), base::Time::Max()); + } + ScheduleVerifyAffiliationsForDummyFacets(&on_startup_verification_timer_, + base::TimeDelta::FromMinutes(1), + VerificationTiming::ON_STARTUP); + ScheduleVerifyAffiliationsForDummyFacets(&repeated_verification_timer_, + base::TimeDelta::FromHours(1), + VerificationTiming::PERIODIC); + } +} + +// static +void AffiliatedMatchHelper::OnRetrievedAffiliationResultsForDummyWebFacets( + VerificationTiming timing, + const AffiliatedFacets& results, + bool success) { + if (timing == AffiliatedMatchHelper::VerificationTiming::ON_STARTUP) { + UMA_HISTOGRAM_BOOLEAN( + "PasswordManager.AffiliationDummyData.RequestSuccess.OnStartup", + success); + if (success) { + UMA_HISTOGRAM_COUNTS_100( + "PasswordManager.AffiliationDummyData.RequestResultCount.OnStartup", + results.size()); + } + } else if (timing == AffiliatedMatchHelper::VerificationTiming::PERIODIC) { + UMA_HISTOGRAM_BOOLEAN( + "PasswordManager.AffiliationDummyData.RequestSuccess.Periodic", + success); + if (success) { + UMA_HISTOGRAM_COUNTS_100( + "PasswordManager.AffiliationDummyData.RequestResultCount.Periodic", + results.size()); + } + } else { + NOTREACHED(); + } } } // namespace password_manager
diff --git a/components/password_manager/core/browser/affiliated_match_helper.h b/components/password_manager/core/browser/affiliated_match_helper.h index 6fa7654..b3ba8860 100644 --- a/components/password_manager/core/browser/affiliated_match_helper.h +++ b/components/password_manager/core/browser/affiliated_match_helper.h
@@ -13,6 +13,7 @@ #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "base/memory/scoped_vector.h" +#include "base/timer/timer.h" #include "components/password_manager/core/browser/affiliation_utils.h" #include "components/password_manager/core/browser/password_store.h" #include "components/password_manager/core/browser/password_store_consumer.h" @@ -68,6 +69,24 @@ const autofill::PasswordForm& observed_form, const AffiliatedRealmsCallback& result_callback); + // Retrieves realms of web sites affiliated with the Android application that + // |android_form| belongs to and invokes |result_callback| on the same thread; + // or yields the empty list if |android_form| is not an Android credential. + // NOTE: This will issue an on-demand network request against the Affiliation + // API if affiliations of the Android application are not cached. However, as + // long as the |android_form| is from the PasswordStore, this should rarely + // happen as affiliation information for those applications are prefetched. + virtual void GetAffiliatedWebRealms( + const autofill::PasswordForm& android_form, + const AffiliatedRealmsCallback& result_callback); + + // Returns whether or not |form| represents an Android credential. + static bool IsValidAndroidCredential(const autofill::PasswordForm& form); + + // Returns whether or not |form| represents a valid Web credential for the + // purposes of affiliation-based matching. + static bool IsValidWebCredential(const autofill::PasswordForm& form); + // Consumes a list of |android_credentials| corresponding to applications that // are affiliated with the realm of |observed_form|, and transforms them so // as to make them fillable into |observed_form|. This can be called from any @@ -90,15 +109,50 @@ static const int64 kInitializationDelayOnStartupInSeconds = 8; private: + // Indicates the time at which verifying that affiliation information has been + // correctly prefetched for dummy Android applications takes place. + enum class VerificationTiming { ON_STARTUP, PERIODIC }; + // Reads all autofillable credentials from the password store and starts // observing the store for future changes. void DoDeferredInitialization(); - // AffiliationService callback: - void OnGetAffiliationsResults(const FacetURI& original_facet_uri, - const AffiliatedRealmsCallback& result_callback, - const AffiliatedFacets& results, - bool success); + // Called back by AffiliationService to supply the list of facets affiliated + // with |original_facet_uri| so that a GetAffiliatedAndroidRealms() call can + // be completed. + void CompleteGetAffiliatedAndroidRealms( + const FacetURI& original_facet_uri, + const AffiliatedRealmsCallback& result_callback, + const AffiliatedFacets& results, + bool success); + + // Called back by AffiliationService to supply the list of facets affiliated + // with the Android application that GetAffiliatedWebRealms() was called with, + // so that the call can be completed. + void CompleteGetAffiliatedWebRealms( + const AffiliatedRealmsCallback& result_callback, + const AffiliatedFacets& results, + bool success); + + // Called either shortly after startup or periodically in the steady state (as + // indicated by |timing|) to verify that affiliation information has been + // correctly prefetched for the dummy Android applications. Will record UMA + // histograms using a appropriate suffix based on |timing|. + void VerifyAffiliationsForDummyFacets(VerificationTiming timing); + + // Sets up the given |timer| to call VerifyAffiliationsForDummyFacets() with + // the specified |delay|, with the given |timing| designation. + void ScheduleVerifyAffiliationsForDummyFacets(base::Timer* timer, + base::TimeDelta delay, + VerificationTiming timing); + + // Called back by the AffiliationService in response to requesting affiliation + // information for the dummy Web facets. The |timing| indicates if the check + // was performed shortly after startup or periodically in the steady state. + static void OnRetrievedAffiliationResultsForDummyWebFacets( + VerificationTiming timing, + const AffiliatedFacets& results, + bool success); // PasswordStore::Observer: void OnLoginsChanged(const PasswordStoreChangeList& changes) override; @@ -113,6 +167,10 @@ // Being the sole consumer of AffiliationService, |this| owns the service. scoped_ptr<AffiliationService> affiliation_service_; + // Timers used to schedule VerifyAffiliationsPrefetchedForDummyFacets(). + base::OneShotTimer<AffiliatedMatchHelper> on_startup_verification_timer_; + base::RepeatingTimer<AffiliatedMatchHelper> repeated_verification_timer_; + base::WeakPtrFactory<AffiliatedMatchHelper> weak_ptr_factory_; DISALLOW_COPY_AND_ASSIGN(AffiliatedMatchHelper);
diff --git a/components/password_manager/core/browser/affiliated_match_helper_unittest.cc b/components/password_manager/core/browser/affiliated_match_helper_unittest.cc index 6f5dc3d0..6ff95ece 100644 --- a/components/password_manager/core/browser/affiliated_match_helper_unittest.cc +++ b/components/password_manager/core/browser/affiliated_match_helper_unittest.cc
@@ -164,6 +164,11 @@ base::RunLoop().RunUntilIdle(); } + void UpdateLogin(const autofill::PasswordForm& form) { + password_store_->UpdateLogin(form); + base::RunLoop().RunUntilIdle(); + } + void RemoveLogin(const autofill::PasswordForm& form) { password_store_->RemoveLogin(form); base::RunLoop().RunUntilIdle(); @@ -227,6 +232,18 @@ return last_result_; } + std::vector<std::string> GetAffiliatedWebRealms( + const autofill::PasswordForm& android_form) { + expecting_result_callback_ = true; + match_helper()->GetAffiliatedWebRealms( + android_form, + base::Bind(&AffiliatedMatchHelperTest::OnAffiliatedRealmsCallback, + base::Unretained(this))); + base::RunLoop().RunUntilIdle(); + EXPECT_FALSE(expecting_result_callback_); + return last_result_; + } + void DestroyMatchHelper() { match_helper_.reset(); } base::TestSimpleTaskRunner* waiting_task_runner() { @@ -354,6 +371,51 @@ testing::IsEmpty()); } +// GetAffiliatedWebRealms* tests verify that GetAffiliatedWebRealms() returns +// the realms of web sites affiliated with the given Android application, but +// only web sites, and only if an Android application is queried. + +TEST_F(AffiliatedMatchHelperTest, GetAffiliatedWebRealmsYieldsResults) { + mock_affiliation_service()->ExpectCallToGetAffiliationsAndSucceedWithResult( + FacetURI::FromCanonicalSpec(kTestAndroidFacetURIAlpha3), + StrategyOnCacheMiss::FETCH_OVER_NETWORK, GetTestEquivalenceClassAlpha()); + autofill::PasswordForm android_form( + GetTestAndroidCredentials(kTestAndroidRealmAlpha3)); + EXPECT_THAT( + GetAffiliatedWebRealms(android_form), + testing::UnorderedElementsAre(kTestWebRealmAlpha1, kTestWebRealmAlpha2)); +} + +TEST_F(AffiliatedMatchHelperTest, GetAffiliatedWebRealmsYieldsOnlyWebsites) { + mock_affiliation_service()->ExpectCallToGetAffiliationsAndSucceedWithResult( + FacetURI::FromCanonicalSpec(kTestAndroidFacetURIBeta2), + StrategyOnCacheMiss::FETCH_OVER_NETWORK, GetTestEquivalenceClassBeta()); + autofill::PasswordForm android_form( + GetTestAndroidCredentials(kTestAndroidRealmBeta2)); + // This verifies that |kTestAndroidRealmBeta3| is not returned. + EXPECT_THAT(GetAffiliatedWebRealms(android_form), + testing::UnorderedElementsAre(kTestWebRealmBeta1)); +} + +TEST_F(AffiliatedMatchHelperTest, + GetAffiliatedWebRealmsYieldsEmptyResultsForWebKeyedForms) { + autofill::PasswordForm web_form( + GetTestObservedWebForm(kTestWebRealmBeta1, nullptr)); + EXPECT_THAT(GetAffiliatedWebRealms(web_form), testing::IsEmpty()); +} + +// Note: IsValidWebCredential() is tested as part of GetAffiliatedAndroidRealms +// tests above. +TEST_F(AffiliatedMatchHelperTest, IsValidAndroidCredential) { + autofill::PasswordForm web_credential( + GetTestObservedWebForm(kTestWebRealmBeta1, nullptr)); + EXPECT_FALSE(AffiliatedMatchHelper::IsValidAndroidCredential(web_credential)); + autofill::PasswordForm android_credential( + GetTestAndroidCredentials(kTestAndroidRealmBeta2)); + EXPECT_TRUE( + AffiliatedMatchHelper::IsValidAndroidCredential(android_credential)); +} + // Verifies that affiliations for Android applications with pre-existing // credentials on start-up are prefetched. TEST_F(AffiliatedMatchHelperTest,
diff --git a/components/password_manager/core/browser/affiliation_backend.cc b/components/password_manager/core/browser/affiliation_backend.cc index fe7e31d..48301dbf 100644 --- a/components/password_manager/core/browser/affiliation_backend.cc +++ b/components/password_manager/core/browser/affiliation_backend.cc
@@ -8,6 +8,7 @@ #include "base/bind.h" #include "base/location.h" +#include "base/metrics/histogram_macros.h" #include "base/single_thread_task_runner.h" #include "base/threading/thread_checker.h" #include "base/time/clock.h" @@ -30,6 +31,7 @@ task_runner_(task_runner), clock_(time_source.Pass()), tick_clock_(time_tick_source.Pass()), + construction_time_(clock_->Now()), weak_ptr_factory_(this) { DCHECK_LT(base::Time(), clock_->Now()); } @@ -225,9 +227,28 @@ fetcher_.reset(AffiliationFetcher::Create(request_context_getter_.get(), requested_facet_uris, this)); fetcher_->StartRequest(); + ReportStatistics(requested_facet_uris.size()); return true; } +void AffiliationBackend::ReportStatistics(size_t requested_facet_uri_count) { + UMA_HISTOGRAM_COUNTS_100("PasswordManager.AffiliationBackend.FetchSize", + requested_facet_uri_count); + + if (last_request_time_.is_null()) { + base::TimeDelta delay = clock_->Now() - construction_time_; + UMA_HISTOGRAM_CUSTOM_TIMES( + "PasswordManager.AffiliationBackend.FirstFetchDelay", delay, + base::TimeDelta::FromSeconds(1), base::TimeDelta::FromDays(3), 50); + } else { + base::TimeDelta delay = clock_->Now() - last_request_time_; + UMA_HISTOGRAM_CUSTOM_TIMES( + "PasswordManager.AffiliationBackend.SubsequentFetchDelay", delay, + base::TimeDelta::FromSeconds(1), base::TimeDelta::FromDays(3), 50); + } + last_request_time_ = clock_->Now(); +} + void AffiliationBackend::SetThrottlerForTesting( scoped_ptr<AffiliationFetchThrottler> throttler) { throttler_ = throttler.Pass();
diff --git a/components/password_manager/core/browser/affiliation_backend.h b/components/password_manager/core/browser/affiliation_backend.h index 88dce8d..334dd264 100644 --- a/components/password_manager/core/browser/affiliation_backend.h +++ b/components/password_manager/core/browser/affiliation_backend.h
@@ -110,6 +110,10 @@ // Returns the number of in-memory FacetManagers. Used only for testing. size_t facet_manager_count_for_testing() { return facet_managers_.size(); } + // Reports the |requested_facet_uri_count| in a single fetch; and the elapsed + // time before the first fetch, and in-between subsequent fetches. + void ReportStatistics(size_t requested_facet_uri_count); + // To be called after Initialize() to use |throttler| instead of the default // one. Used only for testing. void SetThrottlerForTesting(scoped_ptr<AffiliationFetchThrottler> throttler); @@ -127,6 +131,9 @@ scoped_ptr<AffiliationFetcher> fetcher_; scoped_ptr<AffiliationFetchThrottler> throttler_; + base::Time construction_time_; + base::Time last_request_time_; + // Contains a FacetManager for each facet URI that need ongoing attention. To // save memory, managers are discarded as soon as they become redundant. base::ScopedPtrHashMap<FacetURI, FacetManager> facet_managers_;
diff --git a/components/password_manager/core/browser/affiliation_fetcher.cc b/components/password_manager/core/browser/affiliation_fetcher.cc index 9e6bfd0..53069fa 100644 --- a/components/password_manager/core/browser/affiliation_fetcher.cc +++ b/components/password_manager/core/browser/affiliation_fetcher.cc
@@ -4,6 +4,8 @@ #include "components/password_manager/core/browser/affiliation_fetcher.h" +#include "base/metrics/histogram_macros.h" +#include "base/metrics/sparse_histogram.h" #include "components/password_manager/core/browser/affiliation_api.pb.h" #include "components/password_manager/core/browser/affiliation_utils.h" #include "components/password_manager/core/browser/test_affiliation_fetcher_factory.h" @@ -17,6 +19,37 @@ namespace password_manager { +namespace { + +// Enumeration listing the possible outcomes of fetching affiliation information +// from the Affiliation API. This is used in UMA histograms, so do not change +// existing values, only add new values at the end. +enum AffiliationFetchResult { + AFFILIATION_FETCH_RESULT_SUCCESS, + AFFILIATION_FETCH_RESULT_FAILURE, + AFFILIATION_FETCH_RESULT_MALFORMED, + AFFILIATION_FETCH_RESULT_MAX +}; + +// Records the given fetch |result| into the respective UMA histogram, as well +// as the response and error codes of |fetcher| if it is non-null. +void ReportStatistics(AffiliationFetchResult result, + const net::URLFetcher* fetcher) { + UMA_HISTOGRAM_ENUMERATION("PasswordManager.AffiliationFetcher.FetchResult", + result, AFFILIATION_FETCH_RESULT_MAX); + if (fetcher) { + UMA_HISTOGRAM_SPARSE_SLOWLY( + "PasswordManager.AffiliationFetcher.FetchHttpResponseCode", + fetcher->GetResponseCode()); + // Network error codes are negative. See: src/net/base/net_error_list.h. + UMA_HISTOGRAM_SPARSE_SLOWLY( + "PasswordManager.AffiliationFetcher.FetchErrorCode", + -fetcher->GetStatus().error()); + } +} + +} // namespace + static TestAffiliationFetcherFactory* g_testing_factory = nullptr; AffiliationFetcher::AffiliationFetcher( @@ -161,15 +194,21 @@ void AffiliationFetcher::OnURLFetchComplete(const net::URLFetcher* source) { DCHECK_EQ(source, fetcher_.get()); - scoped_ptr<AffiliationFetcherDelegate::Result> result( + // Note that invoking the |delegate_| may destroy |this| synchronously, so the + // invocation must happen last. + scoped_ptr<AffiliationFetcherDelegate::Result> result_data( new AffiliationFetcherDelegate::Result); if (fetcher_->GetStatus().status() == net::URLRequestStatus::SUCCESS && fetcher_->GetResponseCode() == net::HTTP_OK) { - if (ParseResponse(result.get())) - delegate_->OnFetchSucceeded(result.Pass()); - else + if (ParseResponse(result_data.get())) { + ReportStatistics(AFFILIATION_FETCH_RESULT_SUCCESS, nullptr); + delegate_->OnFetchSucceeded(result_data.Pass()); + } else { + ReportStatistics(AFFILIATION_FETCH_RESULT_MALFORMED, nullptr); delegate_->OnMalformedResponse(); + } } else { + ReportStatistics(AFFILIATION_FETCH_RESULT_FAILURE, fetcher_.get()); delegate_->OnFetchFailed(); } }
diff --git a/components/password_manager/core/browser/affiliation_utils.cc b/components/password_manager/core/browser/affiliation_utils.cc index ee1d9b06..fa911ff 100644 --- a/components/password_manager/core/browser/affiliation_utils.cc +++ b/components/password_manager/core/browser/affiliation_utils.cc
@@ -13,6 +13,7 @@ #include "base/strings/string_piece.h" #include "base/strings/string_util.h" #include "components/password_manager/core/common/password_manager_switches.h" +#include "components/variations/variations_associated_data.h" #include "net/base/escape.h" #include "url/third_party/mozilla/url_parse.h" #include "url/url_canon_stdstring.h" @@ -25,6 +26,9 @@ // The scheme used for identifying Android applications. const char kAndroidAppScheme[] = "android"; +// The name of the field trial controlling affiliation-based matching. +const char kFieldTrialName[] = "AffiliationBasedMatching"; + // Returns a StringPiece corresponding to |component| in |uri|, or the empty // string in case there is no such component. base::StringPiece ComponentString(const std::string& uri, @@ -292,7 +296,7 @@ // Note: It is important to always query the field trial state, to ensure that // UMA reports the correct group. const std::string group_name = - base::FieldTrialList::FindFullName("AffiliationBasedMatching"); + base::FieldTrialList::FindFullName(kFieldTrialName); if (command_line.HasSwitch(switches::kDisableAffiliationBasedMatching)) return false; @@ -301,6 +305,32 @@ return StartsWithASCII(group_name, "Enabled", /*case_sensitive=*/false); } +bool IsPropagatingPasswordChangesToWebCredentialsEnabled( + const base::CommandLine& command_line) { + // Note: It is important to always query the variation param first, which, in + // turn, queries the field trial state, which ensures that UMA reports the + // correct group if it is forced by a command line flag. + const std::string update_enabled = variations::GetVariationParamValue( + kFieldTrialName, "propagate_password_changes_to_web"); + + if (command_line.HasSwitch(switches::kDisableAffiliationBasedMatching)) + return false; + if (command_line.HasSwitch(switches::kEnableAffiliationBasedMatching)) + return true; + return LowerCaseEqualsASCII(update_enabled, "enabled"); +} + +bool IsAffiliationRequestsForDummyFacetsEnabled( + const base::CommandLine& command_line) { + const std::string synthesizing_enabled = variations::GetVariationParamValue( + kFieldTrialName, "affiliation_requests_for_dummy_facets"); + if (command_line.HasSwitch(switches::kDisableAffiliationBasedMatching)) + return false; + if (command_line.HasSwitch(switches::kEnableAffiliationBasedMatching)) + return true; + return LowerCaseEqualsASCII(synthesizing_enabled, "enabled"); +} + bool IsValidAndroidFacetURI(const std::string& url) { FacetURI facet = FacetURI::FromPotentiallyInvalidSpec(url); return facet.IsValidAndroidFacetURI();
diff --git a/components/password_manager/core/browser/affiliation_utils.h b/components/password_manager/core/browser/affiliation_utils.h index 1f56c4e8..b5b9546 100644 --- a/components/password_manager/core/browser/affiliation_utils.h +++ b/components/password_manager/core/browser/affiliation_utils.h
@@ -177,6 +177,22 @@ // takes precedence. bool IsAffiliationBasedMatchingEnabled(const base::CommandLine& command_line); +// Returns whether or not propagating password changes to affiliated saved web +// credentials is enabled via variation parameters. This allows disabling only +// the sub-feature while leaving the rest of the affiliation-based matching +// enabled. If the main feature is forced enabled/disabled via the command line, +// the sub-feature will be force enabled/disabled correspondingly. +bool IsPropagatingPasswordChangesToWebCredentialsEnabled( + const base::CommandLine& command_line); + +// Returns whether or not affiliation requests for dummy facets should be +// triggered as part of an experiment to exercise AffiliationService code before +// users would get a chance to have any real Android-based credentials. If the +// main feature is forced enabled/disabled via the command line, the experiment +// is force enabled/disabled correspondingly. +bool IsAffiliationRequestsForDummyFacetsEnabled( + const base::CommandLine& command_line); + // For logging use only. std::ostream& operator<<(std::ostream& os, const FacetURI& facet_uri);
diff --git a/components/password_manager/core/browser/affiliation_utils_unittest.cc b/components/password_manager/core/browser/affiliation_utils_unittest.cc index 16ed69c..73240bc3 100644 --- a/components/password_manager/core/browser/affiliation_utils_unittest.cc +++ b/components/password_manager/core/browser/affiliation_utils_unittest.cc
@@ -7,12 +7,14 @@ #include "base/command_line.h" #include "base/metrics/field_trial.h" #include "components/password_manager/core/common/password_manager_switches.h" +#include "components/variations/variations_associated_data.h" #include "testing/gtest/include/gtest/gtest.h" #include "url/url_constants.h" namespace password_manager { namespace { +const char kFieldTrialName[] = "AffiliationBasedMatching"; const char kTestFacetURI1[] = "https://alpha.example.com/"; const char kTestFacetURI2[] = "https://beta.example.com/"; const char kTestFacetURI3[] = "https://gamma.example.com/"; @@ -196,8 +198,6 @@ } TEST(AffiliationUtilsTest, IsAffiliationBasedMatchingEnabled) { - const char kFieldTrialName[] = "AffiliationBasedMatching"; - struct { const char* field_trial_group; const char* command_line_switch; @@ -235,4 +235,92 @@ } } +TEST(AffiliationUtilsTest, + IsPropagatingPasswordChangesToWebCredentialsEnabled) { + const char kExperimentName[] = "DoesNotMatter"; + + struct { + const char* variation_param; + const char* command_line_switch; + bool expected_enabled; + } kTestCases[] = { + {"", "", false}, + {"", switches::kEnableAffiliationBasedMatching, true}, + {"", switches::kDisableAffiliationBasedMatching, false}, + {"garbage value", "", false}, + {"disabled", "", false}, + {"Disabled", "", false}, + {"Disabled", switches::kDisableAffiliationBasedMatching, false}, + {"Disabled", switches::kEnableAffiliationBasedMatching, true}, + {"enabled", "", true}, + {"Enabled", "", true}, + {"Enabled", switches::kDisableAffiliationBasedMatching, false}, + {"Enabled", switches::kEnableAffiliationBasedMatching, true}}; + + for (const auto& test_case : kTestCases) { + SCOPED_TRACE(testing::Message("Command line = ") + << test_case.command_line_switch); + SCOPED_TRACE(testing::Message("Variation param = ") + << test_case.variation_param); + + variations::testing::ClearAllVariationParams(); + base::FieldTrialList field_trials(nullptr); + base::FieldTrialList::CreateFieldTrial(kFieldTrialName, kExperimentName); + std::map<std::string, std::string> variation_params; + variation_params["propagate_password_changes_to_web"] = + test_case.variation_param; + ASSERT_TRUE(variations::AssociateVariationParams( + kFieldTrialName, kExperimentName, variation_params)); + + base::CommandLine command_line(base::CommandLine::NO_PROGRAM); + command_line.AppendSwitch(test_case.command_line_switch); + EXPECT_EQ( + test_case.expected_enabled, + IsPropagatingPasswordChangesToWebCredentialsEnabled(command_line)); + } +} + +TEST(AffiliationUtilsTest, IsAffiliationRequestsForDummyFacetsEnabled) { + const char kExperimentName[] = "DoesNotMatter"; + + struct { + const char* variation_param; + const char* command_line_switch; + bool expected_enabled; + } kTestCases[] = { + {"", "", false}, + {"", switches::kEnableAffiliationBasedMatching, true}, + {"", switches::kDisableAffiliationBasedMatching, false}, + {"garbage value", "", false}, + {"disabled", "", false}, + {"Disabled", "", false}, + {"Disabled", switches::kDisableAffiliationBasedMatching, false}, + {"Disabled", switches::kEnableAffiliationBasedMatching, true}, + {"enabled", "", true}, + {"Enabled", "", true}, + {"Enabled", switches::kDisableAffiliationBasedMatching, false}, + {"Enabled", switches::kEnableAffiliationBasedMatching, true}}; + + for (const auto& test_case : kTestCases) { + SCOPED_TRACE(testing::Message("Command line = ") + << test_case.command_line_switch); + SCOPED_TRACE(testing::Message("Variation param = ") + << test_case.variation_param); + + variations::testing::ClearAllVariationParams(); + base::FieldTrialList field_trials(nullptr); + base::FieldTrialList::CreateFieldTrial(kFieldTrialName, kExperimentName); + std::map<std::string, std::string> variation_params; + variation_params["affiliation_requests_for_dummy_facets"] = + test_case.variation_param; + ASSERT_TRUE(variations::AssociateVariationParams( + kFieldTrialName, kExperimentName, variation_params)); + + base::CommandLine command_line(base::CommandLine::NO_PROGRAM); + command_line.AppendSwitch(test_case.command_line_switch); + EXPECT_EQ(test_case.expected_enabled, + IsAffiliationRequestsForDummyFacetsEnabled(command_line)); + } +} + } // namespace password_manager
diff --git a/components/password_manager/core/browser/password_generation_manager_unittest.cc b/components/password_manager/core/browser/password_generation_manager_unittest.cc index d01d5f5..eda8962 100644 --- a/components/password_manager/core/browser/password_generation_manager_unittest.cc +++ b/components/password_manager/core/browser/password_generation_manager_unittest.cc
@@ -186,7 +186,7 @@ "<field autofilltype=\"76\" />" "<field autofilltype=\"75\" />" "</autofillqueryresponse>"; - autofill::FormStructure::ParseQueryResponse(kServerResponse, forms); + autofill::FormStructure::ParseQueryResponse(kServerResponse, forms, NULL); DetectAccountCreationForms(forms); EXPECT_EQ(1u, GetTestDriver()->GetFoundAccountCreationForms().size());
diff --git a/components/password_manager/core/browser/password_store.cc b/components/password_manager/core/browser/password_store.cc index f4cd0658..5b13a3c 100644 --- a/components/password_manager/core/browser/password_store.cc +++ b/components/password_manager/core/browser/password_store.cc
@@ -65,6 +65,7 @@ : main_thread_runner_(main_thread_runner), db_thread_runner_(db_thread_runner), observers_(new ObserverListThreadSafe<Observer>()), + is_propagating_password_changes_to_web_credentials_enabled_(false), shutdown_called_(false) { } @@ -218,6 +219,36 @@ num_deletions); } +PasswordStoreChangeList PasswordStore::AddLoginSync(const PasswordForm& form) { + // There is no good way to check if the password is actually up-to-date, or + // at least to check if it was actually changed. Assume it is. + if (AffiliatedMatchHelper::IsValidAndroidCredential(form)) + ScheduleFindAndUpdateAffiliatedWebLogins(form); + return AddLoginImpl(form); +} + +PasswordStoreChangeList PasswordStore::UpdateLoginSync( + const PasswordForm& form) { + if (AffiliatedMatchHelper::IsValidAndroidCredential(form)) { + // Ideally, a |form| would not be updated in any way unless it was ensured + // that it, as a whole, can be used for a successful login. This, sadly, can + // not be guaranteed. It might be that |form| just contains updates to some + // meta-attribute, while it still has an out-of-date password. If such a + // password were to be propagated to affiliated credentials in that case, it + // may very well overwrite the actual, up-to-date password. Try to mitigate + // this risk by ignoring updates unless they actually update the password. + scoped_ptr<PasswordForm> old_form(GetLoginImpl(form)); + if (old_form && form.password_value != old_form->password_value) + ScheduleFindAndUpdateAffiliatedWebLogins(form); + } + return UpdateLoginImpl(form); +} + +PasswordStoreChangeList PasswordStore::RemoveLoginSync( + const PasswordForm& form) { + return RemoveLoginImpl(form); +} + void PasswordStore::NotifyLoginsChanged( const PasswordStoreChangeList& changes) { DCHECK(GetBackgroundTaskRunner()->BelongsToCurrentThread()); @@ -301,6 +332,134 @@ additional_android_realms)); } +scoped_ptr<PasswordForm> PasswordStore::GetLoginImpl( + const PasswordForm& primary_key) { + DCHECK(GetBackgroundTaskRunner()->BelongsToCurrentThread()); + ScopedVector<PasswordForm> candidates( + FillMatchingLogins(primary_key, DISALLOW_PROMPT)); + for (PasswordForm*& candidate : candidates) { + if (candidate->signon_realm == primary_key.signon_realm && + candidate->username_element == primary_key.username_element && + candidate->username_value == primary_key.username_value && + candidate->password_element == primary_key.password_element && + candidate->origin == primary_key.origin && + !candidate->IsPublicSuffixMatch()) { + scoped_ptr<PasswordForm> result(candidate); + candidate = nullptr; + return result.Pass(); + } + } + return make_scoped_ptr<PasswordForm>(nullptr); +} + +void PasswordStore::FindAndUpdateAffiliatedWebLogins( + const PasswordForm& added_or_updated_android_form) { + if (!affiliated_match_helper_ || + !is_propagating_password_changes_to_web_credentials_enabled_) { + return; + } + affiliated_match_helper_->GetAffiliatedWebRealms( + added_or_updated_android_form, + base::Bind(&PasswordStore::ScheduleUpdateAffiliatedWebLoginsImpl, this, + added_or_updated_android_form)); +} + +void PasswordStore::ScheduleFindAndUpdateAffiliatedWebLogins( + const PasswordForm& added_or_updated_android_form) { + main_thread_runner_->PostTask( + FROM_HERE, base::Bind(&PasswordStore::FindAndUpdateAffiliatedWebLogins, + this, added_or_updated_android_form)); +} + +void PasswordStore::UpdateAffiliatedWebLoginsImpl( + const PasswordForm& updated_android_form, + const std::vector<std::string>& affiliated_web_realms) { + DCHECK(GetBackgroundTaskRunner()->BelongsToCurrentThread()); + PasswordStoreChangeList all_changes; + for (const std::string& affiliated_web_realm : affiliated_web_realms) { + PasswordForm web_form_template; + web_form_template.scheme = PasswordForm::SCHEME_HTML; + web_form_template.signon_realm = affiliated_web_realm; + ScopedVector<PasswordForm> web_logins( + FillMatchingLogins(web_form_template, DISALLOW_PROMPT)); + for (PasswordForm* web_login : web_logins) { + // Do not update HTTP logins, logins saved under insecure conditions, and + // non-HTML login forms; PSL matches; logins with a different username; + // and logins with the same password (to avoid generating no-op updates). + if (!AffiliatedMatchHelper::IsValidWebCredential(*web_login) || + web_login->IsPublicSuffixMatch() || + web_login->username_value != updated_android_form.username_value || + web_login->password_value == updated_android_form.password_value) + continue; + + // If the |web_login| was updated in the same or a later chunk of Sync + // changes, assume that it is more recent and do not update it. Note that + // this check is far from perfect conflict resolution and mostly prevents + // long-dormant Sync clients doing damage when they wake up in the face + // of the following list of changes: + // + // Time Source Change + // ==== ====== ====== + // #1 Android android_login.password_value = "A" + // #2 Client A web_login.password_value = "A" (propagation) + // #3 Client A web_login.password_value = "B" (manual overwrite) + // + // When long-dormant Sync client B wakes up, it will only get a distilled + // subset of not-yet-obsoleted changes {1, 3}. In this case, client B must + // not propagate password "A" to |web_login|. This is prevented as change + // #3 will arrive either in the same/later chunk of sync changes, so the + // |date_synced| of |web_login| value will be greater or equal. + // + // Note that this solution has several shortcomings: + // + // (1) It will not prevent local changes to |web_login| from being + // overwritten if they were made shortly after start-up, before + // Sync changes are applied. This should be tolerable. + // + // (2) It assumes that all Sync clients are fully capable of propagating + // changes to web credentials on their own. If client C runs an + // older version of Chrome and updates the password for |web_login| + // around the time when the |android_login| is updated, the updated + // password will not be propagated by client B to |web_login| when + // it wakes up, regardless of the temporal order of the original + // changes, as client B will see both credentials having the same + // |data_synced|. + // + // (2a) Above could be mitigated by looking not only at |data_synced|, + // but also at the actual order of Sync changes. + // + // (2b) However, (2a) is still not workable, as a Sync change is made + // when any attribute of the credential is updated, not only the + // password. Hence it is not possible for client B to distinguish + // between two following two event orders: + // + // #1 Android android_login.password_value = "A" + // #2 Client C web_login.password_value = "B" (manual overwrite) + // #3 Android android_login.random_attribute = "..." + // + // #1 Client C web_login.password_value = "B" (manual overwrite) + // #2 Android android_login.password_value = "A" + // + // And so it must assume that it is unsafe to update |web_login|. + if (web_login->date_synced >= updated_android_form.date_synced) + continue; + + web_login->password_value = updated_android_form.password_value; + + PasswordStoreChangeList changes = UpdateLoginImpl(*web_login); + all_changes.insert(all_changes.end(), changes.begin(), changes.end()); + } + } + NotifyLoginsChanged(all_changes); +} + +void PasswordStore::ScheduleUpdateAffiliatedWebLoginsImpl( + const PasswordForm& updated_android_form, + const std::vector<std::string>& affiliated_web_realms) { + ScheduleTask(base::Bind(&PasswordStore::UpdateAffiliatedWebLoginsImpl, this, + updated_android_form, affiliated_web_realms)); +} + void PasswordStore::InitSyncableService( const syncer::SyncableService::StartSyncFlare& flare) { DCHECK(GetBackgroundTaskRunner()->BelongsToCurrentThread());
diff --git a/components/password_manager/core/browser/password_store.h b/components/password_manager/core/browser/password_store.h index 852a51e..74ff469 100644 --- a/components/password_manager/core/browser/password_store.h +++ b/components/password_manager/core/browser/password_store.h
@@ -6,6 +6,7 @@ #define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_PASSWORD_STORE_H_ #include "base/callback.h" +#include "base/gtest_prod_util.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "base/memory/scoped_vector.h" @@ -74,6 +75,12 @@ // initialized if it is non-null. void SetAffiliatedMatchHelper(scoped_ptr<AffiliatedMatchHelper> helper); + // Toggles whether or not to propagate password changes in Android credentials + // to the affiliated Web credentials. + void enable_propagating_password_changes_to_web_credentials(bool enabled) { + is_propagating_password_changes_to_web_credentials_enabled_ = enabled; + } + // Returns whether or not an affiliation-based match helper is set. bool HasAffiliatedMatchHelper() const; @@ -181,16 +188,6 @@ virtual void ReportMetricsImpl(const std::string& sync_username, bool custom_passphrase_sync_enabled) = 0; - // Bring PasswordStoreSync methods to the scope of PasswordStore. Otherwise, - // base::Bind can't be used with them because it fails to cast PasswordStore - // to PasswordStoreSync. - PasswordStoreChangeList AddLoginImpl( - const autofill::PasswordForm& form) override = 0; - PasswordStoreChangeList UpdateLoginImpl( - const autofill::PasswordForm& form) override = 0; - PasswordStoreChangeList RemoveLoginImpl( - const autofill::PasswordForm& form) override = 0; - // Synchronous implementation to remove the given logins. virtual PasswordStoreChangeList RemoveLoginsCreatedBetweenImpl( base::Time delete_begin, @@ -211,6 +208,20 @@ AuthorizationPromptPolicy prompt_policy, scoped_ptr<GetLoginsRequest> request); + // Synchronous implementation provided by subclasses to add the given login. + virtual PasswordStoreChangeList AddLoginImpl( + const autofill::PasswordForm& form) = 0; + + // Synchronous implementation provided by subclasses to update the given + // login. + virtual PasswordStoreChangeList UpdateLoginImpl( + const autofill::PasswordForm& form) = 0; + + // Synchronous implementation provided by subclasses to remove the given + // login. + virtual PasswordStoreChangeList RemoveLoginImpl( + const autofill::PasswordForm& form) = 0; + // Finds and returns all PasswordForms with the same signon_realm as |form|, // or with a signon_realm that is a PSL-match to that of |form|. virtual ScopedVector<autofill::PasswordForm> FillMatchingLogins( @@ -232,6 +243,13 @@ void LogStatsForBulkDeletionDuringRollback(int num_deletions); // PasswordStoreSync: + PasswordStoreChangeList AddLoginSync( + const autofill::PasswordForm& form) override; + PasswordStoreChangeList UpdateLoginSync( + const autofill::PasswordForm& form) override; + PasswordStoreChangeList RemoveLoginSync( + const autofill::PasswordForm& form) override; + // Called by WrapModificationTask() once the underlying data-modifying // operation has been performed. Notifies observers that password store data // may have been changed. @@ -245,6 +263,10 @@ scoped_refptr<base::SingleThreadTaskRunner> db_thread_runner_; private: + FRIEND_TEST_ALL_PREFIXES(PasswordStoreTest, GetLoginImpl); + FRIEND_TEST_ALL_PREFIXES(PasswordStoreTest, + UpdatePasswordsStoredForAffiliatedWebsites); + // Schedule the given |func| to be run in the PasswordStore's own thread with // responses delivered to |consumer| on the current thread. void Schedule(void (PasswordStore::*func)(scoped_ptr<GetLoginsRequest>), @@ -286,6 +308,38 @@ scoped_ptr<GetLoginsRequest> request, const std::vector<std::string>& additional_android_realms); + // Retrieves the currently stored form, if any, with the same primary key as + // |form|, that is, with the same signon_realm, origin, username_element, + // username_value and password_element attributes. To be called on the + // background thread. + scoped_ptr<autofill::PasswordForm> GetLoginImpl( + const autofill::PasswordForm& primary_key); + + // Called when a password is added or updated for an Android application, and + // triggers finding web sites affiliated with the Android application and + // propagating the new password to credentials for those web sites, if any. + // Called on the main thread. + void FindAndUpdateAffiliatedWebLogins( + const autofill::PasswordForm& added_or_updated_android_form); + + // Posts FindAndUpdateAffiliatedWebLogins() to the main thread. Should be + // called from the background thread. + void ScheduleFindAndUpdateAffiliatedWebLogins( + const autofill::PasswordForm& added_or_updated_android_form); + + // Called when a password is added or updated for an Android application, and + // propagates these changes to credentials stored for |affiliated_web_realms| + // under the same username, if there are any. Called on the background thread. + void UpdateAffiliatedWebLoginsImpl( + const autofill::PasswordForm& updated_android_form, + const std::vector<std::string>& affiliated_web_realms); + + // Schedules UpdateAffiliatedWebLoginsImpl() to run on the background thread. + // Should be called from the main thread. + void ScheduleUpdateAffiliatedWebLoginsImpl( + const autofill::PasswordForm& updated_android_form, + const std::vector<std::string>& affiliated_web_realms); + // Creates PasswordSyncableService instance on the background thread. void InitSyncableService( const syncer::SyncableService::StartSyncFlare& flare); @@ -298,6 +352,7 @@ scoped_ptr<PasswordSyncableService> syncable_service_; scoped_ptr<AffiliatedMatchHelper> affiliated_match_helper_; + bool is_propagating_password_changes_to_web_credentials_enabled_; bool shutdown_called_;
diff --git a/components/password_manager/core/browser/password_store_sync.h b/components/password_manager/core/browser/password_store_sync.h index 731d338..296e71167 100644 --- a/components/password_manager/core/browser/password_store_sync.h +++ b/components/password_manager/core/browser/password_store_sync.h
@@ -28,15 +28,15 @@ WARN_UNUSED_RESULT = 0; // Synchronous implementation to add the given login. - virtual PasswordStoreChangeList AddLoginImpl( + virtual PasswordStoreChangeList AddLoginSync( const autofill::PasswordForm& form) = 0; // Synchronous implementation to update the given login. - virtual PasswordStoreChangeList UpdateLoginImpl( + virtual PasswordStoreChangeList UpdateLoginSync( const autofill::PasswordForm& form) = 0; // Synchronous implementation to remove the given login. - virtual PasswordStoreChangeList RemoveLoginImpl( + virtual PasswordStoreChangeList RemoveLoginSync( const autofill::PasswordForm& form) = 0; // Notifies observers that password store data may have been changed.
diff --git a/components/password_manager/core/browser/password_store_unittest.cc b/components/password_manager/core/browser/password_store_unittest.cc index f5e2a14..6d1a350 100644 --- a/components/password_manager/core/browser/password_store_unittest.cc +++ b/components/password_manager/core/browser/password_store_unittest.cc
@@ -12,7 +12,9 @@ #include "base/files/scoped_temp_dir.h" #include "base/stl_util.h" #include "base/strings/string_util.h" +#include "base/strings/utf_string_conversions.h" #include "base/synchronization/waitable_event.h" +#include "base/thread_task_runner_handle.h" #include "base/time/time.h" #include "components/password_manager/core/browser/affiliated_match_helper.h" #include "components/password_manager/core/browser/affiliation_service.h" @@ -36,6 +38,18 @@ const char kTestWebOrigin1[] = "https://one.example.com/origin"; const char kTestWebRealm2[] = "https://two.example.com/"; const char kTestWebOrigin2[] = "https://two.example.com/origin"; +const char kTestWebRealm3[] = "https://three.example.com/"; +const char kTestWebOrigin3[] = "https://three.example.com/origin"; +const char kTestWebRealm4[] = "https://four.example.com/"; +const char kTestWebOrigin4[] = "https://four.example.com/origin"; +const char kTestWebRealm5[] = "https://five.example.com/"; +const char kTestWebOrigin5[] = "https://five.example.com/origin"; +const char kTestPSLMatchingWebRealm[] = "https://psl.example.com/"; +const char kTestPSLMatchingWebOrigin[] = "https://psl.example.com/origin"; +const char kTestUnrelatedWebRealm[] = "https://notexample.com/"; +const char kTestUnrelatedWebOrigin[] = "https:/notexample.com/origin"; +const char kTestInsecureWebRealm[] = "http://one.example.com/"; +const char kTestInsecureWebOrigin[] = "http://one.example.com/origin"; const char kTestAndroidRealm1[] = "android://hash@com.example.android/"; const char kTestAndroidRealm2[] = "android://hash@com.example.two.android/"; const char kTestAndroidRealm3[] = "android://hash@com.example.three.android/"; @@ -53,6 +67,13 @@ } }; +class MockPasswordStoreObserver + : public password_manager::PasswordStore::Observer { + public: + MOCK_METHOD1(OnLoginsChanged, + void(const password_manager::PasswordStoreChangeList& changes)); +}; + class MockAffiliatedMatchHelper : public AffiliatedMatchHelper { public: MockAffiliatedMatchHelper() @@ -70,9 +91,21 @@ .WillOnce(testing::Return(results_to_return)); } + // Expects GetAffiliatedWebRealms() to be called with the + // |expected_android_form|, and will cause the result callback supplied to + // GetAffiliatedWebRealms() to be invoked with |results_to_return|. + void ExpectCallToGetAffiliatedWebRealms( + const autofill::PasswordForm& expected_android_form, + const std::vector<std::string>& results_to_return) { + EXPECT_CALL(*this, OnGetAffiliatedWebRealmsCalled(expected_android_form)) + .WillOnce(testing::Return(results_to_return)); + } + private: MOCK_METHOD1(OnGetAffiliatedAndroidRealmsCalled, std::vector<std::string>(const PasswordForm&)); + MOCK_METHOD1(OnGetAffiliatedWebRealmsCalled, + std::vector<std::string>(const PasswordForm&)); void GetAffiliatedAndroidRealms( const autofill::PasswordForm& observed_form, @@ -82,6 +115,14 @@ result_callback.Run(affiliated_android_realms); } + void GetAffiliatedWebRealms( + const autofill::PasswordForm& android_form, + const AffiliatedRealmsCallback& result_callback) override { + std::vector<std::string> affiliated_web_realms = + OnGetAffiliatedWebRealmsCalled(android_form); + result_callback.Run(affiliated_web_realms); + } + DISALLOW_COPY_AND_ASSIGN(MockAffiliatedMatchHelper); }; @@ -258,6 +299,56 @@ base::MessageLoop::current()->RunUntilIdle(); } +TEST_F(PasswordStoreTest, GetLoginImpl) { + /* clang-format off */ + static const PasswordFormData kTestCredential = { + PasswordForm::SCHEME_HTML, + kTestWebRealm1, + kTestWebOrigin1, + "", L"", L"username_element", L"password_element", + L"username_value", + L"", true, true, 1}; + /* clang-format on */ + + scoped_refptr<PasswordStoreDefault> store(new PasswordStoreDefault( + base::ThreadTaskRunnerHandle::Get(), base::ThreadTaskRunnerHandle::Get(), + make_scoped_ptr(new LoginDatabase(test_login_db_file_path())))); + store->Init(syncer::SyncableService::StartSyncFlare()); + + // For each attribute in the primary key, create one form that mismatches on + // that attribute. + scoped_ptr<PasswordForm> test_form( + CreatePasswordFormFromDataForTesting(kTestCredential)); + scoped_ptr<PasswordForm> mismatching_form_1(new PasswordForm(*test_form)); + mismatching_form_1->signon_realm = kTestPSLMatchingWebRealm; + scoped_ptr<PasswordForm> mismatching_form_2(new PasswordForm(*test_form)); + mismatching_form_2->origin = GURL(kTestPSLMatchingWebOrigin); + scoped_ptr<PasswordForm> mismatching_form_3(new PasswordForm(*test_form)); + mismatching_form_3->username_element = base::ASCIIToUTF16("other_element"); + scoped_ptr<PasswordForm> mismatching_form_4(new PasswordForm(*test_form)); + mismatching_form_4->password_element = base::ASCIIToUTF16("other_element"); + scoped_ptr<PasswordForm> mismatching_form_5(new PasswordForm(*test_form)); + mismatching_form_5->username_value = + base::ASCIIToUTF16("other_username_value"); + + store->AddLogin(*mismatching_form_1); + store->AddLogin(*mismatching_form_2); + store->AddLogin(*mismatching_form_3); + store->AddLogin(*mismatching_form_4); + store->AddLogin(*mismatching_form_5); + base::MessageLoop::current()->RunUntilIdle(); + EXPECT_FALSE(store->GetLoginImpl(*test_form)); + + store->AddLogin(*test_form); + base::MessageLoop::current()->RunUntilIdle(); + scoped_ptr<PasswordForm> returned_form = store->GetLoginImpl(*test_form); + ASSERT_TRUE(returned_form); + EXPECT_EQ(*test_form, *returned_form); + + store->Shutdown(); + base::MessageLoop::current()->RunUntilIdle(); +} + // When no Android applications are actually affiliated with the realm of the // observed form, GetLoginsWithAffiliations() should still return the exact and // PSL matching results, but not any stored Android credentials. @@ -273,8 +364,8 @@ L"", true, true, 1}, // Credential that is a PSL match of the observed form. {PasswordForm::SCHEME_HTML, - kTestWebRealm2, - kTestWebOrigin2, + kTestPSLMatchingWebRealm, + kTestPSLMatchingWebOrigin, "", L"", L"", L"", L"username_value_2", L"", true, true, 1}, @@ -349,8 +440,8 @@ L"", true, true, 1}, // Credential that is a PSL match of the observed form. {PasswordForm::SCHEME_HTML, - kTestWebRealm2, - kTestWebOrigin2, + kTestPSLMatchingWebRealm, + kTestPSLMatchingWebOrigin, "", L"", L"", L"", L"username_value_2", L"", true, true, 1}, @@ -435,4 +526,222 @@ base::MessageLoop::current()->RunUntilIdle(); } +// This test must use passwords, which are not stored on Mac, therefore the test +// is disabled on Mac. This should not be a huge issue as functionality in the +// platform-independent base class is tested. See also the file-level comment. +#if defined(OS_MACOSX) +#define MAYBE_UpdatePasswordsStoredForAffiliatedWebsites \ + DISABLED_UpdatePasswordsStoredForAffiliatedWebsites +#else +#define MAYBE_UpdatePasswordsStoredForAffiliatedWebsites \ + UpdatePasswordsStoredForAffiliatedWebsites +#endif + +// When the password stored for an Android application is updated, credentials +// with the same username stored for affiliated web sites should also be updated +// automatically. +TEST_F(PasswordStoreTest, MAYBE_UpdatePasswordsStoredForAffiliatedWebsites) { + const wchar_t kTestUsername[] = L"username_value_1"; + const wchar_t kTestOtherUsername[] = L"username_value_2"; + const wchar_t kTestOldPassword[] = L"old_password_value"; + const wchar_t kTestNewPassword[] = L"new_password_value"; + const wchar_t kTestOtherPassword[] = L"other_password_value"; + + /* clang-format off */ + static const PasswordFormData kTestCredentials[] = { + // The credential for the Android application that will be updated + // explicitly so it can be tested if the changes are correctly propagated + // to affiliated Web credentials. + {PasswordForm::SCHEME_HTML, + kTestAndroidRealm1, + "", "", L"", L"", L"", + kTestUsername, + kTestOldPassword, true, true, 2}, + + // --- Positive samples --- Credentials that the password update should be + // automatically propagated to. + + // Credential for an affiliated web site with the same username. + {PasswordForm::SCHEME_HTML, + kTestWebRealm1, + kTestWebOrigin1, + "", L"", L"", L"", + kTestUsername, + kTestOldPassword, true, true, 1}, + // Credential for another affiliated web site with the same username. + // Although the password is different than the current/old password for + // the Android application, it should be updated regardless. + {PasswordForm::SCHEME_HTML, + kTestWebRealm2, + kTestWebOrigin2, + "", L"", L"", L"", + kTestUsername, + kTestOtherPassword, true, true, 1}, + + // --- Negative samples --- Credentials that the password update should + // not be propagated to. + + // Credential for another affiliated web site, but one that already has + // the new password. + {PasswordForm::SCHEME_HTML, + kTestWebRealm3, + kTestWebOrigin3, + "", L"", L"", L"", + kTestUsername, + kTestNewPassword, true, true, 1}, + // Credential for another affiliated web site, but one that was saved + // under insecure conditions. + {PasswordForm::SCHEME_HTML, + kTestWebRealm4, + kTestWebOrigin4, + "", L"", L"", L"", + kTestUsername, + kTestOldPassword, true, false, 1}, + // Credential for the HTTP version of an affiliated web site. + {PasswordForm::SCHEME_HTML, + kTestInsecureWebRealm, + kTestInsecureWebOrigin, + "", L"", L"", L"", + kTestUsername, + kTestOldPassword, true, false, 1}, + // Credential for an affiliated web site, but with a different username. + {PasswordForm::SCHEME_HTML, + kTestWebRealm1, + kTestWebOrigin1, + "", L"", L"", L"", + kTestOtherUsername, + kTestOldPassword, true, true, 1}, + // Credential for a web site that is a PSL match to a web sites affiliated + // with the Android application. + {PasswordForm::SCHEME_HTML, + kTestPSLMatchingWebRealm, + kTestPSLMatchingWebOrigin, + "poisoned", L"poisoned", L"", L"", + kTestUsername, + kTestOldPassword, true, true, 1}, + // Credential for an unrelated web site. + {PasswordForm::SCHEME_HTML, + kTestUnrelatedWebRealm, + kTestUnrelatedWebOrigin, + "", L"", L"", L"", + kTestUsername, + kTestOldPassword, true, true, 1}, + // Credential for an affiliated Android application. + {PasswordForm::SCHEME_HTML, + kTestAndroidRealm2, + "", "", L"", L"", L"", + kTestUsername, + kTestOldPassword, true, true, 1}, + // Credential for an unrelated Android application. + {PasswordForm::SCHEME_HTML, + kTestUnrelatedAndroidRealm, + "", "", L"", L"", L"", + kTestUsername, + kTestOldPassword, true, true, 1}, + // Credential for an affiliated web site with the same username, but one + // that was updated at the same time via Sync as the Android credential. + {PasswordForm::SCHEME_HTML, + kTestWebRealm5, + kTestWebOrigin5, + "", L"", L"", L"", + kTestUsername, + kTestOtherPassword, true, true, 2}}; + /* clang-format on */ + + // The number of positive samples in |kTestCredentials|. + const size_t kExpectedNumberOfPropagatedUpdates = 2u; + + const bool kFalseTrue[] = {false, true}; + for (bool propagation_enabled : kFalseTrue) { + for (bool test_remove_and_add_login : kFalseTrue) { + SCOPED_TRACE(testing::Message("propagation_enabled: ") + << propagation_enabled); + SCOPED_TRACE(testing::Message("test_remove_and_add_login: ") + << test_remove_and_add_login); + + scoped_refptr<PasswordStoreDefault> store(new PasswordStoreDefault( + base::ThreadTaskRunnerHandle::Get(), + base::ThreadTaskRunnerHandle::Get(), + make_scoped_ptr(new LoginDatabase(test_login_db_file_path())))); + store->Init(syncer::SyncableService::StartSyncFlare()); + store->RemoveLoginsCreatedBetween(base::Time(), base::Time::Max()); + + // Set up the initial test data set. + ScopedVector<PasswordForm> all_credentials; + for (size_t i = 0; i < arraysize(kTestCredentials); ++i) { + all_credentials.push_back( + CreatePasswordFormFromDataForTesting(kTestCredentials[i])); + all_credentials.back()->date_synced = + all_credentials.back()->date_created; + store->AddLogin(*all_credentials.back()); + base::MessageLoop::current()->RunUntilIdle(); + } + + // The helper must be injected after the initial test data is set up, + // otherwise it will already start propagating updates as new Android + // credentials are added. + MockAffiliatedMatchHelper* mock_helper = new MockAffiliatedMatchHelper; + store->SetAffiliatedMatchHelper(make_scoped_ptr(mock_helper)); + store->enable_propagating_password_changes_to_web_credentials( + propagation_enabled); + + // Calculate how the correctly updated test data set should look like. + size_t expected_number_of_propageted_updates = + propagation_enabled ? kExpectedNumberOfPropagatedUpdates : 0u; + ScopedVector<PasswordForm> expected_credentials_after_update; + for (size_t i = 0; i < all_credentials.size(); ++i) { + expected_credentials_after_update.push_back( + new autofill::PasswordForm(*all_credentials[i])); + if (i < 1 + expected_number_of_propageted_updates) { + expected_credentials_after_update.back()->password_value = + base::WideToUTF16(kTestNewPassword); + } + } + + if (propagation_enabled) { + std::vector<std::string> affiliated_web_realms; + affiliated_web_realms.push_back(kTestWebRealm1); + affiliated_web_realms.push_back(kTestWebRealm2); + affiliated_web_realms.push_back(kTestWebRealm3); + affiliated_web_realms.push_back(kTestWebRealm4); + affiliated_web_realms.push_back(kTestWebRealm5); + mock_helper->ExpectCallToGetAffiliatedWebRealms( + *expected_credentials_after_update[0], affiliated_web_realms); + } + + // Explicitly update the Android credential, wait until things calm down, + // then query all passwords and expect that: + // 1.) The positive samples in |kTestCredentials| have the new password, + // but the negative samples do not. + // 2.) Change notifications are sent about the updates. Note that as the + // test interacts with the (Update|Add)LoginSync methods, only the + // derived changes should trigger notifications, the first one would + // normally be trigger by Sync. + MockPasswordStoreObserver mock_observer; + store->AddObserver(&mock_observer); + if (propagation_enabled) { + EXPECT_CALL(mock_observer, OnLoginsChanged(testing::SizeIs( + expected_number_of_propageted_updates))); + } + if (test_remove_and_add_login) { + store->RemoveLoginSync(*all_credentials[0]); + store->AddLoginSync(*expected_credentials_after_update[0]); + } else { + store->UpdateLoginSync(*expected_credentials_after_update[0]); + } + base::MessageLoop::current()->RunUntilIdle(); + store->RemoveObserver(&mock_observer); + + MockPasswordStoreConsumer mock_consumer; + EXPECT_CALL( + mock_consumer, + OnGetPasswordStoreResultsConstRef(UnorderedPasswordFormElementsAre( + expected_credentials_after_update.get()))); + store->GetAutofillableLogins(&mock_consumer); + store->Shutdown(); + base::MessageLoop::current()->RunUntilIdle(); + } + } +} + } // namespace password_manager
diff --git a/components/password_manager/core/browser/password_syncable_service.cc b/components/password_manager/core/browser/password_syncable_service.cc index 870aecb..d8ced9d 100644 --- a/components/password_manager/core/browser/password_syncable_service.cc +++ b/components/password_manager/core/browser/password_syncable_service.cc
@@ -260,6 +260,7 @@ AppendPasswordFromSpecifics( specifics.password().client_only_encrypted_data(), time_now, entries); } + WriteToPasswordStore(sync_entries); return syncer::SyncError(); } @@ -335,15 +336,12 @@ void PasswordSyncableService::WriteToPasswordStore(const SyncEntries& entries) { PasswordStoreChangeList changes; - WriteEntriesToDatabase(&PasswordStoreSync::AddLoginImpl, - entries.new_entries.get(), - &changes); - WriteEntriesToDatabase(&PasswordStoreSync::UpdateLoginImpl, - entries.updated_entries.get(), - &changes); - WriteEntriesToDatabase(&PasswordStoreSync::RemoveLoginImpl, - entries.deleted_entries.get(), - &changes); + WriteEntriesToDatabase(&PasswordStoreSync::AddLoginSync, + entries.new_entries.get(), &changes); + WriteEntriesToDatabase(&PasswordStoreSync::UpdateLoginSync, + entries.updated_entries.get(), &changes); + WriteEntriesToDatabase(&PasswordStoreSync::RemoveLoginSync, + entries.deleted_entries.get(), &changes); // We have to notify password store observers of the change by hand since // we use internal password store interfaces to make changes synchronously.
diff --git a/components/printing/renderer/print_web_view_helper.h b/components/printing/renderer/print_web_view_helper.h index d040dbc8..562b1e0 100644 --- a/components/printing/renderer/print_web_view_helper.h +++ b/components/printing/renderer/print_web_view_helper.h
@@ -26,6 +26,17 @@ struct PrintMsg_PrintPages_Params; struct PrintHostMsg_SetOptionsFromDocument_Params; +// RenderVIewTest-based tests crash on Android +// http://crbug.com/187500 +#if defined(OS_ANDROID) +#define MAYBE_PrintWebViewHelperTest DISABLED_PrintWebViewHelperTest +#define MAYBE_PrintWebViewHelperPreviewTest \ + DISABLED_PrintWebViewHelperPreviewTest +#else +#define MAYBE_PrintWebViewHelperTest PrintWebViewHelperTest +#define MAYBE_PrintWebViewHelperPreviewTest PrintWebViewHelperPreviewTest +#endif // defined(OS_ANDROID) + namespace base { class DictionaryValue; } @@ -115,16 +126,16 @@ private: friend class PrintWebViewHelperTestBase; - FRIEND_TEST_ALL_PREFIXES(PrintWebViewHelperPreviewTest, + FRIEND_TEST_ALL_PREFIXES(MAYBE_PrintWebViewHelperPreviewTest, BlockScriptInitiatedPrinting); - FRIEND_TEST_ALL_PREFIXES(PrintWebViewHelperTest, OnPrintPages); - FRIEND_TEST_ALL_PREFIXES(PrintWebViewHelperTest, + FRIEND_TEST_ALL_PREFIXES(MAYBE_PrintWebViewHelperTest, OnPrintPages); + FRIEND_TEST_ALL_PREFIXES(MAYBE_PrintWebViewHelperTest, BlockScriptInitiatedPrinting); - FRIEND_TEST_ALL_PREFIXES(PrintWebViewHelperTest, + FRIEND_TEST_ALL_PREFIXES(MAYBE_PrintWebViewHelperTest, BlockScriptInitiatedPrintingFromPopup); #if defined(OS_WIN) || defined(OS_MACOSX) - FRIEND_TEST_ALL_PREFIXES(PrintWebViewHelperTest, PrintLayoutTest); - FRIEND_TEST_ALL_PREFIXES(PrintWebViewHelperTest, PrintWithIframe); + FRIEND_TEST_ALL_PREFIXES(MAYBE_PrintWebViewHelperTest, PrintLayoutTest); + FRIEND_TEST_ALL_PREFIXES(MAYBE_PrintWebViewHelperTest, PrintWithIframe); #endif // defined(OS_WIN) || defined(OS_MACOSX) enum PrintingResult {
diff --git a/components/printing/test/print_web_view_helper_browsertest.cc b/components/printing/test/print_web_view_helper_browsertest.cc index bfdc311..65ecc23 100644 --- a/components/printing/test/print_web_view_helper_browsertest.cc +++ b/components/printing/test/print_web_view_helper_browsertest.cc
@@ -248,22 +248,30 @@ DISALLOW_COPY_AND_ASSIGN(PrintWebViewHelperTestBase); }; -class PrintWebViewHelperTest : public PrintWebViewHelperTestBase { +// RenderVIewTest-based tests crash on Android +// http://crbug.com/187500 +#if defined(OS_ANDROID) +#define MAYBE_PrintWebViewHelperTest DISABLED_PrintWebViewHelperTest +#else +#define MAYBE_PrintWebViewHelperTest PrintWebViewHelperTest +#endif // defined(OS_ANDROID) + +class MAYBE_PrintWebViewHelperTest : public PrintWebViewHelperTestBase { public: - PrintWebViewHelperTest() {} - ~PrintWebViewHelperTest() override {} + MAYBE_PrintWebViewHelperTest() {} + ~MAYBE_PrintWebViewHelperTest() override {} void SetUp() override { PrintWebViewHelperTestBase::SetUp(); } protected: - DISALLOW_COPY_AND_ASSIGN(PrintWebViewHelperTest); + DISALLOW_COPY_AND_ASSIGN(MAYBE_PrintWebViewHelperTest); }; // This tests only for platforms without print preview. #if !defined(ENABLE_PRINT_PREVIEW) // Tests that the renderer blocks window.print() calls if they occur too // frequently. -TEST_F(PrintWebViewHelperTest, BlockScriptInitiatedPrinting) { +TEST_F(MAYBE_PrintWebViewHelperTest, BlockScriptInitiatedPrinting) { // Pretend user will cancel printing. print_render_thread_->set_print_dialog_user_response(false); // Try to print with window.print() a few times. @@ -287,7 +295,7 @@ // Tests that the renderer always allows window.print() calls if they are user // initiated. -TEST_F(PrintWebViewHelperTest, AllowUserOriginatedPrinting) { +TEST_F(MAYBE_PrintWebViewHelperTest, AllowUserOriginatedPrinting) { // Pretend user will cancel printing. print_render_thread_->set_print_dialog_user_response(false); // Try to print with window.print() a few times. @@ -325,7 +333,7 @@ } // Duplicate of OnPrintPagesTest only using javascript to print. -TEST_F(PrintWebViewHelperTest, PrintWithJavascript) { +TEST_F(MAYBE_PrintWebViewHelperTest, PrintWithJavascript) { PrintWithJavaScript(); VerifyPageCount(1); @@ -336,7 +344,7 @@ #if defined(ENABLE_BASIC_PRINTING) // Tests that printing pages work and sending and receiving messages through // that channel all works. -TEST_F(PrintWebViewHelperTest, OnPrintPages) { +TEST_F(MAYBE_PrintWebViewHelperTest, OnPrintPages) { LoadHTML(kHelloWorldHTML); OnPrintPages(); @@ -350,7 +358,7 @@ // to rip out and replace most of the IPC code if we ever plan to improve // printing, and the comment below by sverrir suggests that it doesn't do much // for us anyway. -TEST_F(PrintWebViewHelperTest, PrintWithIframe) { +TEST_F(MAYBE_PrintWebViewHelperTest, PrintWithIframe) { // Document that populates an iframe. const char html[] = "<html><body>Lorem Ipsum:" @@ -440,7 +448,7 @@ // metafile directly. // Same for printing via PDF on Windows. #if defined(OS_MACOSX) && defined(ENABLE_BASIC_PRINTING) -TEST_F(PrintWebViewHelperTest, PrintLayoutTest) { +TEST_F(MAYBE_PrintWebViewHelperTest, PrintLayoutTest) { bool baseline = false; EXPECT_TRUE(print_render_thread_->printer() != NULL); @@ -496,10 +504,20 @@ // These print preview tests do not work on Chrome OS yet. #if !defined(OS_CHROMEOS) -class PrintWebViewHelperPreviewTest : public PrintWebViewHelperTestBase { + +// RenderVIewTest-based tests crash on Android +// http://crbug.com/187500 +#if defined(OS_ANDROID) +#define MAYBE_PrintWebViewHelperPreviewTest \ + DISABLED_PrintWebViewHelperPreviewTest +#else +#define MAYBE_PrintWebViewHelperPreviewTest PrintWebViewHelperPreviewTest +#endif // defined(OS_ANDROID) + +class MAYBE_PrintWebViewHelperPreviewTest : public PrintWebViewHelperTestBase { public: - PrintWebViewHelperPreviewTest() {} - ~PrintWebViewHelperPreviewTest() override {} + MAYBE_PrintWebViewHelperPreviewTest() {} + ~MAYBE_PrintWebViewHelperPreviewTest() override {} protected: void VerifyPrintPreviewCancelled(bool did_cancel) { @@ -591,11 +609,11 @@ } } - DISALLOW_COPY_AND_ASSIGN(PrintWebViewHelperPreviewTest); + DISALLOW_COPY_AND_ASSIGN(MAYBE_PrintWebViewHelperPreviewTest); }; #if defined(ENABLE_PRINT_PREVIEW) -TEST_F(PrintWebViewHelperPreviewTest, BlockScriptInitiatedPrinting) { +TEST_F(MAYBE_PrintWebViewHelperPreviewTest, BlockScriptInitiatedPrinting) { LoadHTML(kHelloWorldHTML); PrintWebViewHelper* print_web_view_helper = PrintWebViewHelper::Get(view_); print_web_view_helper->SetScriptedPrintBlocked(true); @@ -607,7 +625,7 @@ VerifyPreviewRequest(true); } -TEST_F(PrintWebViewHelperPreviewTest, PrintWithJavaScript) { +TEST_F(MAYBE_PrintWebViewHelperPreviewTest, PrintWithJavaScript) { LoadHTML(kPrintOnUserAction); gfx::Size new_size(200, 100); Resize(new_size, gfx::Rect(), false); @@ -630,7 +648,7 @@ // Tests that print preview work and sending and receiving messages through // that channel all works. -TEST_F(PrintWebViewHelperPreviewTest, OnPrintPreview) { +TEST_F(MAYBE_PrintWebViewHelperPreviewTest, OnPrintPreview) { LoadHTML(kHelloWorldHTML); // Fill in some dummy values. @@ -646,7 +664,8 @@ VerifyPagesPrinted(false); } -TEST_F(PrintWebViewHelperPreviewTest, PrintPreviewHTMLWithPageMarginsCss) { +TEST_F(MAYBE_PrintWebViewHelperPreviewTest, + PrintPreviewHTMLWithPageMarginsCss) { // A simple web page with print margins css. const char kHTMLWithPageMarginsCss[] = "<html><head><style>" @@ -677,7 +696,8 @@ // Test to verify that print preview ignores print media css when non-default // margin is selected. -TEST_F(PrintWebViewHelperPreviewTest, NonDefaultMarginsSelectedIgnorePrintCss) { +TEST_F(MAYBE_PrintWebViewHelperPreviewTest, + NonDefaultMarginsSelectedIgnorePrintCss) { LoadHTML(kHTMLWithPageSizeCss); // Fill in some dummy values. @@ -697,7 +717,7 @@ // Test to verify that print preview honor print media size css when // PRINT_TO_PDF is selected and doesn't fit to printer default paper size. -TEST_F(PrintWebViewHelperPreviewTest, PrintToPDFSelectedHonorPrintCss) { +TEST_F(MAYBE_PrintWebViewHelperPreviewTest, PrintToPDFSelectedHonorPrintCss) { LoadHTML(kHTMLWithPageSizeCss); // Fill in some dummy values. @@ -717,7 +737,8 @@ // Test to verify that print preview honor print margin css when PRINT_TO_PDF // is selected and doesn't fit to printer default paper size. -TEST_F(PrintWebViewHelperPreviewTest, PrintToPDFSelectedHonorPageMarginsCss) { +TEST_F(MAYBE_PrintWebViewHelperPreviewTest, + PrintToPDFSelectedHonorPageMarginsCss) { // A simple web page with print margins css. const char kHTMLWithPageCss[] = "<html><head><style>" @@ -749,7 +770,7 @@ // Test to verify that print preview workflow center the html page contents to // fit the page size. -TEST_F(PrintWebViewHelperPreviewTest, PrintPreviewCenterToFitPage) { +TEST_F(MAYBE_PrintWebViewHelperPreviewTest, PrintPreviewCenterToFitPage) { LoadHTML(kHTMLWithPageSizeCss); // Fill in some dummy values. @@ -768,7 +789,7 @@ // Test to verify that print preview workflow scale the html page contents to // fit the page size. -TEST_F(PrintWebViewHelperPreviewTest, PrintPreviewShrinkToFitPage) { +TEST_F(MAYBE_PrintWebViewHelperPreviewTest, PrintPreviewShrinkToFitPage) { // A simple web page with print margins css. const char kHTMLWithPageCss[] = "<html><head><style>" @@ -797,7 +818,7 @@ // Test to verify that print preview workflow honor the orientation settings // specified in css. -TEST_F(PrintWebViewHelperPreviewTest, PrintPreviewHonorsOrientationCss) { +TEST_F(MAYBE_PrintWebViewHelperPreviewTest, PrintPreviewHonorsOrientationCss) { LoadHTML(kHTMLWithLandscapePageCss); // Fill in some dummy values. @@ -815,7 +836,8 @@ // Test to verify that print preview workflow honors the orientation settings // specified in css when PRINT_TO_PDF is selected. -TEST_F(PrintWebViewHelperPreviewTest, PrintToPDFSelectedHonorOrientationCss) { +TEST_F(MAYBE_PrintWebViewHelperPreviewTest, + PrintToPDFSelectedHonorOrientationCss) { LoadHTML(kHTMLWithLandscapePageCss); // Fill in some dummy values. @@ -833,7 +855,7 @@ // Test to verify that complete metafile is generated for a subset of pages // without creating draft pages. -TEST_F(PrintWebViewHelperPreviewTest, OnPrintPreviewForSelectedPages) { +TEST_F(MAYBE_PrintWebViewHelperPreviewTest, OnPrintPreviewForSelectedPages) { LoadHTML(kMultipageHTML); // Fill in some dummy values. @@ -866,7 +888,7 @@ } // Test to verify that preview generated only for one page. -TEST_F(PrintWebViewHelperPreviewTest, OnPrintPreviewForSelectedText) { +TEST_F(MAYBE_PrintWebViewHelperPreviewTest, OnPrintPreviewForSelectedText) { LoadHTML(kMultipageHTML); GetMainFrame()->selectRange( blink::WebRange::fromDocumentRange(GetMainFrame(), 1, 3)); @@ -887,7 +909,7 @@ // Tests that print preview fails and receiving error messages through // that channel all works. -TEST_F(PrintWebViewHelperPreviewTest, OnPrintPreviewFail) { +TEST_F(MAYBE_PrintWebViewHelperPreviewTest, OnPrintPreviewFail) { LoadHTML(kHelloWorldHTML); // An empty dictionary should fail. @@ -902,7 +924,7 @@ } // Tests that cancelling print preview works. -TEST_F(PrintWebViewHelperPreviewTest, OnPrintPreviewCancel) { +TEST_F(MAYBE_PrintWebViewHelperPreviewTest, OnPrintPreviewCancel) { LoadHTML(kLongPageHTML); const int kCancelPage = 3; @@ -921,7 +943,7 @@ // Tests that printing from print preview works and sending and receiving // messages through that channel all works. -TEST_F(PrintWebViewHelperPreviewTest, OnPrintForPrintPreview) { +TEST_F(MAYBE_PrintWebViewHelperPreviewTest, OnPrintForPrintPreview) { LoadHTML(kPrintPreviewHTML); // Fill in some dummy values. @@ -935,7 +957,7 @@ // Tests that printing from print preview fails and receiving error messages // through that channel all works. -TEST_F(PrintWebViewHelperPreviewTest, OnPrintForPrintPreviewFail) { +TEST_F(MAYBE_PrintWebViewHelperPreviewTest, OnPrintForPrintPreviewFail) { LoadHTML(kPrintPreviewHTML); // An empty dictionary should fail. @@ -947,7 +969,7 @@ // Tests that when default printer has invalid printer settings, print preview // receives error message. -TEST_F(PrintWebViewHelperPreviewTest, +TEST_F(MAYBE_PrintWebViewHelperPreviewTest, OnPrintPreviewUsingInvalidPrinterSettings) { LoadHTML(kPrintPreviewHTML); @@ -970,7 +992,8 @@ // Tests that when the selected printer has invalid page settings, print preview // receives error message. -TEST_F(PrintWebViewHelperPreviewTest, OnPrintPreviewUsingInvalidPageSize) { +TEST_F(MAYBE_PrintWebViewHelperPreviewTest, + OnPrintPreviewUsingInvalidPageSize) { LoadHTML(kPrintPreviewHTML); print_render_thread_->printer()->UseInvalidPageSize(); @@ -989,7 +1012,8 @@ // Tests that when the selected printer has invalid content settings, print // preview receives error message. -TEST_F(PrintWebViewHelperPreviewTest, OnPrintPreviewUsingInvalidContentSize) { +TEST_F(MAYBE_PrintWebViewHelperPreviewTest, + OnPrintPreviewUsingInvalidContentSize) { LoadHTML(kPrintPreviewHTML); print_render_thread_->printer()->UseInvalidContentSize(); @@ -1006,7 +1030,7 @@ VerifyPrintPreviewGenerated(false); } -TEST_F(PrintWebViewHelperPreviewTest, +TEST_F(MAYBE_PrintWebViewHelperPreviewTest, OnPrintForPrintPreviewUsingInvalidPrinterSettings) { LoadHTML(kPrintPreviewHTML);
diff --git a/components/proximity_auth.gypi b/components/proximity_auth.gypi index c4e2503..7da4644c 100644 --- a/components/proximity_auth.gypi +++ b/components/proximity_auth.gypi
@@ -23,6 +23,8 @@ "proximity_auth/bluetooth_util.cc", "proximity_auth/bluetooth_util.h", "proximity_auth/bluetooth_util_chromeos.cc", + "proximity_auth/ble/proximity_auth_ble_system.cc", + "proximity_auth/ble/proximity_auth_ble_system.h", "proximity_auth/client.cc", "proximity_auth/client.h", "proximity_auth/client_observer.h",
diff --git a/components/proximity_auth/ble/BUILD.gn b/components/proximity_auth/ble/BUILD.gn new file mode 100644 index 0000000..db2d0db --- /dev/null +++ b/components/proximity_auth/ble/BUILD.gn
@@ -0,0 +1,17 @@ +# 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. + +source_set("ble") { + sources = [ + "proximity_auth_ble_system.cc", + "proximity_auth_ble_system.h", + ] + + deps = [ + "//base", + "//components/proximity_auth", + "//device/bluetooth", + "//net", + ] +}
diff --git a/components/proximity_auth/ble/OWNERS b/components/proximity_auth/ble/OWNERS new file mode 100644 index 0000000..e0920d6 --- /dev/null +++ b/components/proximity_auth/ble/OWNERS
@@ -0,0 +1,2 @@ +msarda@chromium.org +sacomoto@chromium.org
diff --git a/components/proximity_auth/ble/proximity_auth_ble_system.cc b/components/proximity_auth/ble/proximity_auth_ble_system.cc new file mode 100644 index 0000000..b4f20e4 --- /dev/null +++ b/components/proximity_auth/ble/proximity_auth_ble_system.cc
@@ -0,0 +1,19 @@ +// 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 "components/proximity_auth/ble/proximity_auth_ble_system.h" + +#include "base/logging.h" + +namespace proximity_auth { + +ProximityAuthBleSystem::ProximityAuthBleSystem() { + VLOG(1) << "Starting Proximity Auth over Bluetooth Low Energy."; +} + +ProximityAuthBleSystem::~ProximityAuthBleSystem() { + VLOG(1) << "Stopping Proximity over Bluetooth Low Energy."; +} + +} // namespace proximity_auth
diff --git a/components/proximity_auth/ble/proximity_auth_ble_system.h b/components/proximity_auth/ble/proximity_auth_ble_system.h new file mode 100644 index 0000000..ab59bae --- /dev/null +++ b/components/proximity_auth/ble/proximity_auth_ble_system.h
@@ -0,0 +1,27 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_PROXIMITY_AUTH_BLE_PROXIMITY_AUTH_BLE_SYSTEM_H_ +#define COMPONENTS_PROXIMITY_AUTH_BLE_PROXIMITY_AUTH_BLE_SYSTEM_H_ + +#include "base/macros.h" + +namespace proximity_auth { + +// This is the main entry point to start Proximity Auth over Bluetooth Low +// Energy. This is the underlying system for the Smart Lock features. It will +// discover Bluetooth Low Energy phones and unlock the lock screen if the phone +// passes an authorization and authentication protocol. +class ProximityAuthBleSystem { + public: + ProximityAuthBleSystem(); + ~ProximityAuthBleSystem(); + + private: + DISALLOW_COPY_AND_ASSIGN(ProximityAuthBleSystem); +}; + +} // namespace proximity_auth + +#endif // COMPONENTS_PROXIMITY_AUTH_BLE_PROXIMITY_AUTH_BLE_SYSTEM_H_
diff --git a/components/proximity_auth/switches.cc b/components/proximity_auth/switches.cc index 5e63e9b0..9d1577fe 100644 --- a/components/proximity_auth/switches.cc +++ b/components/proximity_auth/switches.cc
@@ -14,6 +14,10 @@ // Disable Easy unlock. const char kDisableEasyUnlock[] = "disable-easy-unlock"; +// Enables discovery of the phone over Bluetooth Low Energy. +const char kEnableBluetoothLowEnergyDiscovery[] = + "enable-proximity-auth-bluetooth-low-energy-discovery"; + // Enables close proximity detection. This allows the user to set a setting to // require very close proximity between the remote device and the local device // in order to unlock the local device, which trades off convenience for
diff --git a/components/proximity_auth/switches.h b/components/proximity_auth/switches.h index 04f6162..b58cb5e7 100644 --- a/components/proximity_auth/switches.h +++ b/components/proximity_auth/switches.h
@@ -12,6 +12,7 @@ // alongside the definition of their values in the .cc file. extern const char kCryptAuthHTTPHost[]; extern const char kDisableEasyUnlock[]; +extern const char kEnableBluetoothLowEnergyDiscovery[]; extern const char kEnableProximityDetection[]; extern const char kForceLoadEasyUnlockAppInTests[];
diff --git a/components/scheduler/BUILD.gn b/components/scheduler/BUILD.gn new file mode 100644 index 0000000..2d00aef --- /dev/null +++ b/components/scheduler/BUILD.gn
@@ -0,0 +1,56 @@ +# 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("//components/scheduler/scheduler.gni") + +# GYP version: components/scheduler.gypi:scheduler +component("scheduler") { + sources = rebase_path(scheduler_gypi_values.scheduler_sources, + ".", + "//components/scheduler") + + defines = [ "SCHEDULER_IMPLEMENTATION" ] + + # TODO(jschuh): crbug.com/167187 fix size_t to int truncations. + configs += [ "//build/config/compiler:no_size_t_to_int_warning" ] + + deps = [ + ":common", + "//base", + "//cc:cc", + "//third_party/WebKit/public:blink", + "//ui/gfx:gfx", + ] +} + +# GYP version: components/scheduler.gypi:scheduler_common +source_set("common") { + sources = rebase_path(scheduler_gypi_values.scheduler_common_sources, + ".", + "//components/scheduler") +} + +source_set("unit_tests") { + testonly = true + + sources = [ + "child/nestable_task_runner_for_test.cc", + "child/nestable_task_runner_for_test.h", + "child/prioritizing_task_queue_selector_unittest.cc", + "child/scheduler_helper_unittest.cc", + "child/task_queue_manager_unittest.cc", + "child/test_time_source.cc", + "child/test_time_source.h", + "child/worker_scheduler_impl_unittest.cc", + "renderer/deadline_task_runner_unittest.cc", + "renderer/renderer_scheduler_impl_unittest.cc", + ] + + deps = [ + ":scheduler", + "//cc:test_support", + "//testing/gmock", + "//testing/gtest", + ] +}
diff --git a/components/scheduler/DEPS b/components/scheduler/DEPS new file mode 100644 index 0000000..a298a17 --- /dev/null +++ b/components/scheduler/DEPS
@@ -0,0 +1,4 @@ +include_rules = [ + "-components/scheduler", + "+components/scheduler/common", +]
diff --git a/content/child/scheduler/OWNERS b/components/scheduler/OWNERS similarity index 100% rename from content/child/scheduler/OWNERS rename to components/scheduler/OWNERS
diff --git a/components/scheduler/child/DEPS b/components/scheduler/child/DEPS new file mode 100644 index 0000000..f4ac360 --- /dev/null +++ b/components/scheduler/child/DEPS
@@ -0,0 +1,12 @@ +include_rules = [ + "+components/scheduler/common", + "+components/scheduler/scheduler_export.h", + "+third_party/WebKit/public/platform", +] + +specific_include_rules = { + "(test_time_source|.*test)\.cc": [ + "+cc/test", + "+content/test", + ], +}
diff --git a/content/child/scheduler/OWNERS b/components/scheduler/child/OWNERS similarity index 100% copy from content/child/scheduler/OWNERS copy to components/scheduler/child/OWNERS
diff --git a/content/child/scheduler/cancelable_closure_holder.cc b/components/scheduler/child/cancelable_closure_holder.cc similarity index 85% rename from content/child/scheduler/cancelable_closure_holder.cc rename to components/scheduler/child/cancelable_closure_holder.cc index 09e688d..0e1660a 100644 --- a/content/child/scheduler/cancelable_closure_holder.cc +++ b/components/scheduler/child/cancelable_closure_holder.cc
@@ -2,9 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "content/child/scheduler/cancelable_closure_holder.h" +#include "components/scheduler/child/cancelable_closure_holder.h" -namespace content { +namespace scheduler { CancelableClosureHolder::CancelableClosureHolder() { } @@ -27,4 +27,4 @@ return cancelable_callback_.callback(); } -} // namespace content +} // namespace scheduler
diff --git a/content/child/scheduler/cancelable_closure_holder.h b/components/scheduler/child/cancelable_closure_holder.h similarity index 80% rename from content/child/scheduler/cancelable_closure_holder.h rename to components/scheduler/child/cancelable_closure_holder.h index 1e3892cc..20968a4 100644 --- a/content/child/scheduler/cancelable_closure_holder.h +++ b/components/scheduler/child/cancelable_closure_holder.h
@@ -2,12 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CONTENT_CHILD_SCHEDULER_CANCELABLE_CLOSURE_HOLDER_H_ -#define CONTENT_CHILD_SCHEDULER_CANCELABLE_CLOSURE_HOLDER_H_ +#ifndef COMPONENTS_SCHEDULER_CHILD_CANCELABLE_CLOSURE_HOLDER_H_ +#define COMPONENTS_SCHEDULER_CHILD_CANCELABLE_CLOSURE_HOLDER_H_ #include "base/cancelable_callback.h" -namespace content { +namespace scheduler { // A CancelableClosureHolder is a CancelableCallback which resets its wrapped // callback with a cached closure whenever it is canceled. @@ -34,6 +34,6 @@ DISALLOW_COPY_AND_ASSIGN(CancelableClosureHolder); }; -} // namespace content +} // namespace scheduler -#endif // CONTENT_CHILD_SCHEDULER_CANCELABLE_CLOSURE_HOLDER_H_ +#endif // COMPONENTS_SCHEDULER_CHILD_CANCELABLE_CLOSURE_HOLDER_H_
diff --git a/content/child/scheduler/child_scheduler.h b/components/scheduler/child/child_scheduler.h similarity index 84% rename from content/child/scheduler/child_scheduler.h rename to components/scheduler/child/child_scheduler.h index 308dc1c..8fd5359 100644 --- a/content/child/scheduler/child_scheduler.h +++ b/components/scheduler/child/child_scheduler.h
@@ -2,22 +2,22 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CONTENT_CHILD_SCHEDULER_CHILD_SCHEDULER_H_ -#define CONTENT_CHILD_SCHEDULER_CHILD_SCHEDULER_H_ +#ifndef COMPONENTS_SCHEDULER_CHILD_CHILD_SCHEDULER_H_ +#define COMPONENTS_SCHEDULER_CHILD_CHILD_SCHEDULER_H_ #include "base/message_loop/message_loop.h" -#include "content/child/scheduler/single_thread_idle_task_runner.h" -#include "content/common/content_export.h" +#include "components/scheduler/child/single_thread_idle_task_runner.h" +#include "components/scheduler/scheduler_export.h" namespace base { class MessageLoop; } -namespace content { +namespace scheduler { -class CONTENT_EXPORT ChildScheduler { +class SCHEDULER_EXPORT ChildScheduler { public: - virtual ~ChildScheduler() { } + virtual ~ChildScheduler() {} // Returns the default task runner. virtual scoped_refptr<base::SingleThreadTaskRunner> DefaultTaskRunner() = 0; @@ -58,10 +58,10 @@ virtual void Shutdown() = 0; protected: - ChildScheduler() { } + ChildScheduler() {} DISALLOW_COPY_AND_ASSIGN(ChildScheduler); }; -} // namespace content +} // namespace scheduler -#endif // CONTENT_CHILD_SCHEDULER_CHILD_SCHEDULER_H_ +#endif // COMPONENTS_SCHEDULER_CHILD_CHILD_SCHEDULER_H_
diff --git a/content/child/scheduler/nestable_single_thread_task_runner.h b/components/scheduler/child/nestable_single_thread_task_runner.h similarity index 63% rename from content/child/scheduler/nestable_single_thread_task_runner.h rename to components/scheduler/child/nestable_single_thread_task_runner.h index dbacaf8..e1bf990 100644 --- a/content/child/scheduler/nestable_single_thread_task_runner.h +++ b/components/scheduler/child/nestable_single_thread_task_runner.h
@@ -2,16 +2,16 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CONTENT_CHILD_SCHEDULER_NESTABLE_SINGLE_THREAD_TASK_RUNNER_H_ -#define CONTENT_CHILD_SCHEDULER_NESTABLE_SINGLE_THREAD_TASK_RUNNER_H_ +#ifndef COMPONENTS_SCHEDULER_CHILD_NESTABLE_SINGLE_THREAD_TASK_RUNNER_H_ +#define COMPONENTS_SCHEDULER_CHILD_NESTABLE_SINGLE_THREAD_TASK_RUNNER_H_ #include "base/single_thread_task_runner.h" -#include "content/common/content_export.h" +#include "components/scheduler/scheduler_export.h" -namespace content { +namespace scheduler { // A single thread task runner which exposes whether it is running nested. -class CONTENT_EXPORT NestableSingleThreadTaskRunner +class SCHEDULER_EXPORT NestableSingleThreadTaskRunner : public base::SingleThreadTaskRunner { public: NestableSingleThreadTaskRunner() {} @@ -26,6 +26,6 @@ DISALLOW_COPY_AND_ASSIGN(NestableSingleThreadTaskRunner); }; -} // namespace content +} // namespace scheduler -#endif // CONTENT_CHILD_SCHEDULER_NESTABLE_SINGLE_THREAD_TASK_RUNNER_H_ +#endif // COMPONENTS_SCHEDULER_CHILD_NESTABLE_SINGLE_THREAD_TASK_RUNNER_H_
diff --git a/content/child/scheduler/nestable_task_runner_for_test.cc b/components/scheduler/child/nestable_task_runner_for_test.cc similarity index 91% rename from content/child/scheduler/nestable_task_runner_for_test.cc rename to components/scheduler/child/nestable_task_runner_for_test.cc index abab53bb..42e1264d 100644 --- a/content/child/scheduler/nestable_task_runner_for_test.cc +++ b/components/scheduler/child/nestable_task_runner_for_test.cc
@@ -2,9 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "content/child/scheduler/nestable_task_runner_for_test.h" +#include "components/scheduler/child/nestable_task_runner_for_test.h" -namespace content { +namespace scheduler { // static scoped_refptr<NestableTaskRunnerForTest> NestableTaskRunnerForTest::Create( @@ -46,4 +46,4 @@ is_nested_ = is_nested; } -} // namespace content +} // namespace scheduler
diff --git a/content/child/scheduler/nestable_task_runner_for_test.h b/components/scheduler/child/nestable_task_runner_for_test.h similarity index 91% rename from content/child/scheduler/nestable_task_runner_for_test.h rename to components/scheduler/child/nestable_task_runner_for_test.h index c9b9b96..69056204 100644 --- a/content/child/scheduler/nestable_task_runner_for_test.h +++ b/components/scheduler/child/nestable_task_runner_for_test.h
@@ -5,9 +5,9 @@ #ifndef CONTENT_RENDERER_SCHEDULER_NESTABLE_TASK_RUNNER_FOR_TEST_H_ #define CONTENT_RENDERER_SCHEDULER_NESTABLE_TASK_RUNNER_FOR_TEST_H_ -#include "content/child/scheduler/nestable_single_thread_task_runner.h" +#include "components/scheduler/child/nestable_single_thread_task_runner.h" -namespace content { +namespace scheduler { class NestableTaskRunnerForTest : public NestableSingleThreadTaskRunner { public: @@ -39,6 +39,6 @@ DISALLOW_COPY_AND_ASSIGN(NestableTaskRunnerForTest); }; -} // namespace content +} // namespace scheduler #endif // CONTENT_RENDERER_SCHEDULER_NESTABLE_TASK_RUNNER_FOR_TEST_H_
diff --git a/content/child/scheduler/null_idle_task_runner.cc b/components/scheduler/child/null_idle_task_runner.cc similarity index 80% rename from content/child/scheduler/null_idle_task_runner.cc rename to components/scheduler/child/null_idle_task_runner.cc index 08818ce..9d11e7c 100644 --- a/content/child/scheduler/null_idle_task_runner.cc +++ b/components/scheduler/child/null_idle_task_runner.cc
@@ -2,9 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "content/child/scheduler/null_idle_task_runner.h" +#include "components/scheduler/child/null_idle_task_runner.h" -namespace content { +namespace scheduler { NullIdleTaskRunner::NullIdleTaskRunner() : SingleThreadIdleTaskRunner(nullptr, @@ -27,8 +27,8 @@ } void NullIdleTaskRunner::PostIdleTaskAfterWakeup( - const tracked_objects::Location& from_here, - const IdleTask& idle_task) { + const tracked_objects::Location& from_here, + const IdleTask& idle_task) { } -} // namespace content +} // namespace scheduler
diff --git a/components/scheduler/child/null_idle_task_runner.h b/components/scheduler/child/null_idle_task_runner.h new file mode 100644 index 0000000..734d0c1 --- /dev/null +++ b/components/scheduler/child/null_idle_task_runner.h
@@ -0,0 +1,33 @@ +// 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 COMPONENTS_SCHEDULER_CHILD_NULL_IDLE_TASK_RUNNER_H_ +#define COMPONENTS_SCHEDULER_CHILD_NULL_IDLE_TASK_RUNNER_H_ + +#include "components/scheduler/child/single_thread_idle_task_runner.h" + +namespace scheduler { + +class NullIdleTaskRunner : public SingleThreadIdleTaskRunner { + public: + NullIdleTaskRunner(); + void PostIdleTask(const tracked_objects::Location& from_here, + const IdleTask& idle_task) override; + + void PostNonNestableIdleTask(const tracked_objects::Location& from_here, + const IdleTask& idle_task) override; + + void PostIdleTaskAfterWakeup(const tracked_objects::Location& from_here, + const IdleTask& idle_task) override; + + protected: + ~NullIdleTaskRunner() override; + + private: + DISALLOW_COPY_AND_ASSIGN(NullIdleTaskRunner); +}; + +} // namespace scheduler + +#endif // COMPONENTS_SCHEDULER_CHILD_NULL_IDLE_TASK_RUNNER_H_
diff --git a/content/child/scheduler/null_worker_scheduler.cc b/components/scheduler/child/null_worker_scheduler.cc similarity index 78% rename from content/child/scheduler/null_worker_scheduler.cc rename to components/scheduler/child/null_worker_scheduler.cc index 3127c3f1..4abfb0e 100644 --- a/content/child/scheduler/null_worker_scheduler.cc +++ b/components/scheduler/child/null_worker_scheduler.cc
@@ -2,16 +2,17 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "content/child/scheduler/null_worker_scheduler.h" +#include "components/scheduler/child/null_worker_scheduler.h" #include "base/bind.h" -#include "base/message_loop/message_loop_proxy.h" -#include "content/child/scheduler/null_idle_task_runner.h" +#include "base/message_loop/message_loop.h" +#include "base/thread_task_runner_handle.h" +#include "components/scheduler/child/null_idle_task_runner.h" -namespace content { +namespace scheduler { NullWorkerScheduler::NullWorkerScheduler() - : task_runner_(base::MessageLoopProxy::current()), + : task_runner_(base::ThreadTaskRunnerHandle::Get()), idle_task_runner_(new NullIdleTaskRunner()) { } @@ -52,4 +53,4 @@ void NullWorkerScheduler::Shutdown() { } -} // namespace content +} // namespace scheduler
diff --git a/content/child/scheduler/null_worker_scheduler.h b/components/scheduler/child/null_worker_scheduler.h similarity index 76% rename from content/child/scheduler/null_worker_scheduler.h rename to components/scheduler/child/null_worker_scheduler.h index a03d6b2..535fa08 100644 --- a/content/child/scheduler/null_worker_scheduler.h +++ b/components/scheduler/child/null_worker_scheduler.h
@@ -2,12 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CONTENT_CHILD_SCHEDULER_NULL_WORKER_SCHEDULER_H_ -#define CONTENT_CHILD_SCHEDULER_NULL_WORKER_SCHEDULER_H_ +#ifndef COMPONENTS_SCHEDULER_CHILD_NULL_WORKER_SCHEDULER_H_ +#define COMPONENTS_SCHEDULER_CHILD_NULL_WORKER_SCHEDULER_H_ -#include "content/child/scheduler/worker_scheduler.h" +#include "components/scheduler/child/worker_scheduler.h" -namespace content { +namespace scheduler { class NullWorkerScheduler : public WorkerScheduler { public: @@ -32,6 +32,6 @@ DISALLOW_COPY_AND_ASSIGN(NullWorkerScheduler); }; -} // namespace content +} // namespace scheduler -#endif // CONTENT_CHILD_SCHEDULER_NULL_WORKER_SCHEDULER_H_ +#endif // COMPONENTS_SCHEDULER_CHILD_NULL_WORKER_SCHEDULER_H_
diff --git a/content/child/scheduler/prioritizing_task_queue_selector.cc b/components/scheduler/child/prioritizing_task_queue_selector.cc similarity index 97% rename from content/child/scheduler/prioritizing_task_queue_selector.cc rename to components/scheduler/child/prioritizing_task_queue_selector.cc index 193e81ff..5275f72 100644 --- a/content/child/scheduler/prioritizing_task_queue_selector.cc +++ b/components/scheduler/child/prioritizing_task_queue_selector.cc
@@ -2,13 +2,13 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "content/child/scheduler/prioritizing_task_queue_selector.h" +#include "components/scheduler/child/prioritizing_task_queue_selector.h" #include "base/logging.h" #include "base/pending_task.h" #include "base/trace_event/trace_event_argument.h" -namespace content { +namespace scheduler { PrioritizingTaskQueueSelector::PrioritizingTaskQueueSelector() : starvation_count_(0), task_queue_selector_observer_(nullptr) { @@ -189,4 +189,4 @@ task_queue_selector_observer_ = observer; } -} // namespace content +} // namespace scheduler
diff --git a/content/child/scheduler/prioritizing_task_queue_selector.h b/components/scheduler/child/prioritizing_task_queue_selector.h similarity index 90% rename from content/child/scheduler/prioritizing_task_queue_selector.h rename to components/scheduler/child/prioritizing_task_queue_selector.h index 936f2ae..a9a63eb 100644 --- a/content/child/scheduler/prioritizing_task_queue_selector.h +++ b/components/scheduler/child/prioritizing_task_queue_selector.h
@@ -2,21 +2,21 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CONTENT_CHILD_SCHEDULER_PRIORITIZING_TASK_QUEUE_SELECTOR_H_ -#define CONTENT_CHILD_SCHEDULER_PRIORITIZING_TASK_QUEUE_SELECTOR_H_ +#ifndef COMPONENTS_SCHEDULER_CHILD_PRIORITIZING_TASK_QUEUE_SELECTOR_H_ +#define COMPONENTS_SCHEDULER_CHILD_PRIORITIZING_TASK_QUEUE_SELECTOR_H_ #include <set> #include "base/compiler_specific.h" #include "base/threading/thread_checker.h" -#include "content/child/scheduler/task_queue_selector.h" -#include "content/common/content_export.h" +#include "components/scheduler/child/task_queue_selector.h" +#include "components/scheduler/scheduler_export.h" -namespace content { +namespace scheduler { // A PrioritizingTaskQueueSelector is a TaskQueueSelector which is used by the // SchedulerHelper to enable prioritization of particular task queues. -class CONTENT_EXPORT PrioritizingTaskQueueSelector +class SCHEDULER_EXPORT PrioritizingTaskQueueSelector : NON_EXPORTED_BASE(public TaskQueueSelector) { public: enum QueuePriority { @@ -103,6 +103,6 @@ DISALLOW_COPY_AND_ASSIGN(PrioritizingTaskQueueSelector); }; -} // namespace content +} // namespace scheduler -#endif // CONTENT_CHILD_SCHEDULER_PRIORITIZING_TASK_QUEUE_SELECTOR_H_ +#endif // COMPONENTS_SCHEDULER_CHILD_PRIORITIZING_TASK_QUEUE_SELECTOR_H_
diff --git a/content/child/scheduler/prioritizing_task_queue_selector_unittest.cc b/components/scheduler/child/prioritizing_task_queue_selector_unittest.cc similarity index 97% rename from content/child/scheduler/prioritizing_task_queue_selector_unittest.cc rename to components/scheduler/child/prioritizing_task_queue_selector_unittest.cc index f055f3d..81a2a945 100644 --- a/content/child/scheduler/prioritizing_task_queue_selector_unittest.cc +++ b/components/scheduler/child/prioritizing_task_queue_selector_unittest.cc
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "content/child/scheduler/prioritizing_task_queue_selector.h" +#include "components/scheduler/child/prioritizing_task_queue_selector.h" #include "base/bind.h" #include "base/memory/scoped_ptr.h" @@ -11,10 +11,9 @@ #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" -namespace content { +namespace scheduler { -class MockObserver - : public TaskQueueSelector::Observer { +class MockObserver : public TaskQueueSelector::Observer { public: MockObserver() {} virtual ~MockObserver() {} @@ -246,4 +245,4 @@ } } -} // namespace content +} // namespace scheduler
diff --git a/content/child/scheduler/scheduler_helper.cc b/components/scheduler/child/scheduler_helper.cc similarity index 94% rename from content/child/scheduler/scheduler_helper.cc rename to components/scheduler/child/scheduler_helper.cc index 91f4abbd..19e8d2b6 100644 --- a/content/child/scheduler/scheduler_helper.cc +++ b/components/scheduler/child/scheduler_helper.cc
@@ -2,15 +2,15 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "content/child/scheduler/scheduler_helper.h" +#include "components/scheduler/child/scheduler_helper.h" #include "base/trace_event/trace_event.h" #include "base/trace_event/trace_event_argument.h" -#include "content/child/scheduler/nestable_single_thread_task_runner.h" -#include "content/child/scheduler/prioritizing_task_queue_selector.h" -#include "content/child/scheduler/time_source.h" +#include "components/scheduler/child/nestable_single_thread_task_runner.h" +#include "components/scheduler/child/prioritizing_task_queue_selector.h" +#include "components/scheduler/child/time_source.h" -namespace content { +namespace scheduler { SchedulerHelper::SchedulerHelper( scoped_refptr<NestableSingleThreadTaskRunner> main_task_runner, @@ -204,8 +204,7 @@ ComputeNewLongIdlePeriodState(now, &next_long_idle_period_delay); if (IsInIdlePeriod(new_idle_period_state)) { StartIdlePeriod(new_idle_period_state, now, - now + next_long_idle_period_delay, - false); + now + next_long_idle_period_delay, false); } if (task_queue_manager_->IsQueueEmpty(QueueId::IDLE_TASK_QUEUE)) { @@ -264,10 +263,9 @@ idle_period_deadline_ = idle_period_deadline; if (post_end_idle_period) { - control_task_runner_->PostDelayedTask( - FROM_HERE, - end_idle_period_closure_.callback(), - idle_period_deadline_ - now); + control_task_runner_->PostDelayedTask(FROM_HERE, + end_idle_period_closure_.callback(), + idle_period_deadline_ - now); } } @@ -333,13 +331,13 @@ return time_source_->Now(); } -SchedulerHelper::IdlePeriodState -SchedulerHelper::SchedulerIdlePeriodState() const { +SchedulerHelper::IdlePeriodState SchedulerHelper::SchedulerIdlePeriodState() + const { return idle_period_state_; } -PrioritizingTaskQueueSelector* -SchedulerHelper::SchedulerTaskQueueSelector() const { +PrioritizingTaskQueueSelector* SchedulerHelper::SchedulerTaskQueueSelector() + const { return task_queue_selector_.get(); } @@ -410,4 +408,4 @@ task_queue_manager_->RemoveTaskObserver(task_observer); } -} // namespace content +} // namespace scheduler
diff --git a/content/child/scheduler/scheduler_helper.h b/components/scheduler/child/scheduler_helper.h similarity index 92% rename from content/child/scheduler/scheduler_helper.h rename to components/scheduler/child/scheduler_helper.h index 0e03aa9..7ace694 100644 --- a/content/child/scheduler/scheduler_helper.h +++ b/components/scheduler/child/scheduler_helper.h
@@ -2,24 +2,25 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CONTENT_CHILD_SCHEDULER_SCHEDULER_HELPER_H_ -#define CONTENT_CHILD_SCHEDULER_SCHEDULER_HELPER_H_ +#ifndef COMPONENTS_SCHEDULER_CHILD_SCHEDULER_HELPER_H_ +#define COMPONENTS_SCHEDULER_CHILD_SCHEDULER_HELPER_H_ -#include "content/child/scheduler/cancelable_closure_holder.h" -#include "content/child/scheduler/single_thread_idle_task_runner.h" -#include "content/child/scheduler/task_queue_manager.h" -#include "content/child/scheduler/time_source.h" +#include "components/scheduler/child/cancelable_closure_holder.h" +#include "components/scheduler/child/single_thread_idle_task_runner.h" +#include "components/scheduler/child/task_queue_manager.h" +#include "components/scheduler/child/time_source.h" +#include "components/scheduler/scheduler_export.h" -namespace content { +namespace scheduler { class PrioritizingTaskQueueSelector; class NestableSingleThreadTaskRunner; // Common scheduler functionality for Default and Idle tasks. -class CONTENT_EXPORT SchedulerHelper { +class SCHEDULER_EXPORT SchedulerHelper { public: // Used to by scheduler implementations to customize idle behaviour. - class CONTENT_EXPORT SchedulerHelperDelegate { + class SCHEDULER_EXPORT SchedulerHelperDelegate { public: SchedulerHelperDelegate(); virtual ~SchedulerHelperDelegate(); @@ -209,6 +210,6 @@ DISALLOW_COPY_AND_ASSIGN(SchedulerHelper); }; -} // namespace content +} // namespace scheduler -#endif // CONTENT_CHILD_SCHEDULER_SCHEDULER_HELPER_H_ +#endif // COMPONENTS_SCHEDULER_CHILD_SCHEDULER_HELPER_H_
diff --git a/content/child/scheduler/scheduler_helper_unittest.cc b/components/scheduler/child/scheduler_helper_unittest.cc similarity index 88% rename from content/child/scheduler/scheduler_helper_unittest.cc rename to components/scheduler/child/scheduler_helper_unittest.cc index d3e9bda..0418c0be 100644 --- a/content/child/scheduler/scheduler_helper_unittest.cc +++ b/components/scheduler/child/scheduler_helper_unittest.cc
@@ -2,15 +2,15 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "content/child/scheduler/scheduler_helper.h" +#include "components/scheduler/child/scheduler_helper.h" #include "base/callback.h" #include "cc/test/ordered_simple_task_runner.h" #include "cc/test/test_now_source.h" -#include "content/child/scheduler/nestable_task_runner_for_test.h" -#include "content/child/scheduler/scheduler_message_loop_delegate.h" -#include "content/child/scheduler/task_queue_manager.h" -#include "content/test/test_time_source.h" +#include "components/scheduler/child/nestable_task_runner_for_test.h" +#include "components/scheduler/child/scheduler_message_loop_delegate.h" +#include "components/scheduler/child/task_queue_manager.h" +#include "components/scheduler/child/test_time_source.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -19,7 +19,7 @@ using testing::Invoke; using testing::Return; -namespace content { +namespace scheduler { namespace { void AppendToVectorTestTask(std::vector<std::string>* vector, @@ -36,17 +36,16 @@ void NullTask() { } -void AppendToVectorReentrantTask( - base::SingleThreadTaskRunner* task_runner, - std::vector<int>* vector, - int* reentrant_count, - int max_reentrant_count) { +void AppendToVectorReentrantTask(base::SingleThreadTaskRunner* task_runner, + std::vector<int>* vector, + int* reentrant_count, + int max_reentrant_count) { vector->push_back((*reentrant_count)++); if (*reentrant_count < max_reentrant_count) { task_runner->PostTask( - FROM_HERE, base::Bind(AppendToVectorReentrantTask, - base::Unretained(task_runner), vector, - reentrant_count, max_reentrant_count)); + FROM_HERE, + base::Bind(AppendToVectorReentrantTask, base::Unretained(task_runner), + vector, reentrant_count, max_reentrant_count)); } } @@ -62,15 +61,13 @@ int max_idle_task_reposts = 2; -void RepostingIdleTestTask( - SingleThreadIdleTaskRunner* idle_task_runner, - int* run_count, - base::TimeTicks deadline) { +void RepostingIdleTestTask(SingleThreadIdleTaskRunner* idle_task_runner, + int* run_count, + base::TimeTicks deadline) { if ((*run_count + 1) < max_idle_task_reposts) { idle_task_runner->PostIdleTask( - FROM_HERE, - base::Bind(&RepostingIdleTestTask, - base::Unretained(idle_task_runner), run_count)); + FROM_HERE, base::Bind(&RepostingIdleTestTask, + base::Unretained(idle_task_runner), run_count)); } (*run_count)++; } @@ -284,10 +281,8 @@ EXPECT_EQ(0, run_count); scheduler_helper_->StartIdlePeriod( - SchedulerHelper::IdlePeriodState::IN_SHORT_IDLE_PERIOD, - clock_->Now(), - expected_deadline, - true); + SchedulerHelper::IdlePeriodState::IN_SHORT_IDLE_PERIOD, clock_->Now(), + expected_deadline, true); RunUntilIdle(); EXPECT_EQ(1, run_count); EXPECT_EQ(expected_deadline, deadline_in_task); @@ -305,10 +300,8 @@ EXPECT_EQ(0, run_count); scheduler_helper_->StartIdlePeriod( - SchedulerHelper::IdlePeriodState::IN_SHORT_IDLE_PERIOD, - clock_->Now(), - clock_->Now() + base::TimeDelta::FromMilliseconds(10), - true); + SchedulerHelper::IdlePeriodState::IN_SHORT_IDLE_PERIOD, clock_->Now(), + clock_->Now() + base::TimeDelta::FromMilliseconds(10), true); scheduler_helper_->EndIdlePeriod(); RunUntilIdle(); EXPECT_EQ(0, run_count); @@ -322,10 +315,8 @@ FROM_HERE, base::Bind(&RepostingIdleTestTask, idle_task_runner_, &run_count)); scheduler_helper_->StartIdlePeriod( - SchedulerHelper::IdlePeriodState::IN_SHORT_IDLE_PERIOD, - clock_->Now(), - clock_->Now() + base::TimeDelta::FromMilliseconds(10), - true); + SchedulerHelper::IdlePeriodState::IN_SHORT_IDLE_PERIOD, clock_->Now(), + clock_->Now() + base::TimeDelta::FromMilliseconds(10), true); RunUntilIdle(); EXPECT_EQ(1, run_count); @@ -334,10 +325,8 @@ EXPECT_EQ(1, run_count); scheduler_helper_->StartIdlePeriod( - SchedulerHelper::IdlePeriodState::IN_SHORT_IDLE_PERIOD, - clock_->Now(), - clock_->Now() + base::TimeDelta::FromMilliseconds(10), - true); + SchedulerHelper::IdlePeriodState::IN_SHORT_IDLE_PERIOD, clock_->Now(), + clock_->Now() + base::TimeDelta::FromMilliseconds(10), true); RunUntilIdle(); EXPECT_EQ(2, run_count); } @@ -354,20 +343,16 @@ default_task_runner_, &run_count)); scheduler_helper_->StartIdlePeriod( - SchedulerHelper::IdlePeriodState::IN_SHORT_IDLE_PERIOD, - clock_->Now(), - clock_->Now() + base::TimeDelta::FromMilliseconds(10), - true); + SchedulerHelper::IdlePeriodState::IN_SHORT_IDLE_PERIOD, clock_->Now(), + clock_->Now() + base::TimeDelta::FromMilliseconds(10), true); RunUntilIdle(); // Only the first idle task should execute since it's used up the deadline. EXPECT_EQ(1, run_count); scheduler_helper_->EndIdlePeriod(); scheduler_helper_->StartIdlePeriod( - SchedulerHelper::IdlePeriodState::IN_SHORT_IDLE_PERIOD, - clock_->Now(), - clock_->Now() + base::TimeDelta::FromMilliseconds(10), - true); + SchedulerHelper::IdlePeriodState::IN_SHORT_IDLE_PERIOD, clock_->Now(), + clock_->Now() + base::TimeDelta::FromMilliseconds(10), true); RunUntilIdle(); // Second task should be run on the next idle period. EXPECT_EQ(2, run_count); @@ -381,10 +366,8 @@ FROM_HERE, base::Bind(&IdleTestTask, &run_count, &deadline_in_task)); scheduler_helper_->StartIdlePeriod( - SchedulerHelper::IdlePeriodState::IN_SHORT_IDLE_PERIOD, - clock_->Now(), - clock_->Now() + base::TimeDelta::FromMilliseconds(10), - true); + SchedulerHelper::IdlePeriodState::IN_SHORT_IDLE_PERIOD, clock_->Now(), + clock_->Now() + base::TimeDelta::FromMilliseconds(10), true); RunUntilIdle(); // Shouldn't run yet as no other task woke up the scheduler. EXPECT_EQ(0, run_count); @@ -393,10 +376,8 @@ FROM_HERE, base::Bind(&IdleTestTask, &run_count, &deadline_in_task)); scheduler_helper_->StartIdlePeriod( - SchedulerHelper::IdlePeriodState::IN_SHORT_IDLE_PERIOD, - clock_->Now(), - clock_->Now() + base::TimeDelta::FromMilliseconds(10), - true); + SchedulerHelper::IdlePeriodState::IN_SHORT_IDLE_PERIOD, clock_->Now(), + clock_->Now() + base::TimeDelta::FromMilliseconds(10), true); RunUntilIdle(); // Another after wakeup idle task shouldn't wake the scheduler. EXPECT_EQ(0, run_count); @@ -406,10 +387,8 @@ RunUntilIdle(); // Must start a new idle period before idle task runs. scheduler_helper_->StartIdlePeriod( - SchedulerHelper::IdlePeriodState::IN_SHORT_IDLE_PERIOD, - clock_->Now(), - clock_->Now() + base::TimeDelta::FromMilliseconds(10), - true); + SchedulerHelper::IdlePeriodState::IN_SHORT_IDLE_PERIOD, clock_->Now(), + clock_->Now() + base::TimeDelta::FromMilliseconds(10), true); RunUntilIdle(); // Execution of default task queue task should trigger execution of idle task. EXPECT_EQ(2, run_count); @@ -426,10 +405,8 @@ RunUntilIdle(); // Must start a new idle period before idle task runs. scheduler_helper_->StartIdlePeriod( - SchedulerHelper::IdlePeriodState::IN_SHORT_IDLE_PERIOD, - clock_->Now(), - clock_->Now() + base::TimeDelta::FromMilliseconds(10), - true); + SchedulerHelper::IdlePeriodState::IN_SHORT_IDLE_PERIOD, clock_->Now(), + clock_->Now() + base::TimeDelta::FromMilliseconds(10), true); RunUntilIdle(); // Should run as the scheduler was already awakened by the normal task. EXPECT_EQ(1, run_count); @@ -445,17 +422,13 @@ FROM_HERE, base::Bind(&IdleTestTask, &run_count, &deadline_in_task)); scheduler_helper_->StartIdlePeriod( - SchedulerHelper::IdlePeriodState::IN_SHORT_IDLE_PERIOD, - clock_->Now(), - clock_->Now() + base::TimeDelta::FromMilliseconds(10), - true); + SchedulerHelper::IdlePeriodState::IN_SHORT_IDLE_PERIOD, clock_->Now(), + clock_->Now() + base::TimeDelta::FromMilliseconds(10), true); RunUntilIdle(); // Must start a new idle period before after-wakeup idle task runs. scheduler_helper_->StartIdlePeriod( - SchedulerHelper::IdlePeriodState::IN_SHORT_IDLE_PERIOD, - clock_->Now(), - clock_->Now() + base::TimeDelta::FromMilliseconds(10), - true); + SchedulerHelper::IdlePeriodState::IN_SHORT_IDLE_PERIOD, clock_->Now(), + clock_->Now() + base::TimeDelta::FromMilliseconds(10), true); RunUntilIdle(); // Normal idle task should wake up after-wakeup idle task. EXPECT_EQ(2, run_count); @@ -527,10 +500,8 @@ } } scheduler_helper_->StartIdlePeriod( - SchedulerHelper::IdlePeriodState::IN_SHORT_IDLE_PERIOD, - clock_->Now(), - clock_->Now() + base::TimeDelta::FromMilliseconds(10), - true); + SchedulerHelper::IdlePeriodState::IN_SHORT_IDLE_PERIOD, clock_->Now(), + clock_->Now() + base::TimeDelta::FromMilliseconds(10), true); message_loop_->RunUntilIdle(); } @@ -565,10 +536,8 @@ base::Unretained(&tasks_to_post_from_nested_loop))); scheduler_helper_->StartIdlePeriod( - SchedulerHelper::IdlePeriodState::IN_SHORT_IDLE_PERIOD, - clock_->Now(), - clock_->Now() + base::TimeDelta::FromMilliseconds(10), - true); + SchedulerHelper::IdlePeriodState::IN_SHORT_IDLE_PERIOD, clock_->Now(), + clock_->Now() + base::TimeDelta::FromMilliseconds(10), true); RunUntilIdle(); // Note we expect task 3 to run last because it's non-nestable. EXPECT_THAT(order, testing::ElementsAre(std::string("1"), std::string("2"), @@ -727,14 +696,14 @@ int run_count = 0; ON_CALL(*scheduler_helper_, CanEnterLongIdlePeriod(_, _)) - .WillByDefault(Invoke([delay, delayOver]( - base::TimeTicks now, - base::TimeDelta* next_long_idle_period_delay_out) { - if (now >= delayOver) - return true; - *next_long_idle_period_delay_out = delay; - return false; - })); + .WillByDefault(Invoke( + [delay, delayOver](base::TimeTicks now, + base::TimeDelta* next_long_idle_period_delay_out) { + if (now >= delayOver) + return true; + *next_long_idle_period_delay_out = delay; + return false; + })); EXPECT_CALL(*scheduler_helper_, CanEnterLongIdlePeriod(_, _)).Times(3); @@ -781,10 +750,8 @@ scheduler_helper_.get(), &can_exceed_idle_deadline, &run_count)); scheduler_helper_->StartIdlePeriod( - SchedulerHelper::IdlePeriodState::IN_SHORT_IDLE_PERIOD, - clock_->Now(), - clock_->Now() + base::TimeDelta::FromMilliseconds(10), - true); + SchedulerHelper::IdlePeriodState::IN_SHORT_IDLE_PERIOD, clock_->Now(), + clock_->Now() + base::TimeDelta::FromMilliseconds(10), true); RunUntilIdle(); EXPECT_EQ(1, run_count); EXPECT_FALSE(can_exceed_idle_deadline); @@ -945,4 +912,4 @@ EXPECT_EQ(expected_deadline, deadline_in_task); } -} // namespace content +} // namespace scheduler
diff --git a/content/child/scheduler/scheduler_message_loop_delegate.cc b/components/scheduler/child/scheduler_message_loop_delegate.cc similarity index 91% rename from content/child/scheduler/scheduler_message_loop_delegate.cc rename to components/scheduler/child/scheduler_message_loop_delegate.cc index 32fdc087..8c06e30 100644 --- a/content/child/scheduler/scheduler_message_loop_delegate.cc +++ b/components/scheduler/child/scheduler_message_loop_delegate.cc
@@ -2,9 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "content/child/scheduler/scheduler_message_loop_delegate.h" +#include "components/scheduler/child/scheduler_message_loop_delegate.h" -namespace content { +namespace scheduler { // static scoped_refptr<SchedulerMessageLoopDelegate> @@ -43,4 +43,4 @@ return message_loop_->IsNested(); } -} // namespace content +} // namespace scheduler
diff --git a/content/child/scheduler/scheduler_message_loop_delegate.h b/components/scheduler/child/scheduler_message_loop_delegate.h similarity index 73% rename from content/child/scheduler/scheduler_message_loop_delegate.h rename to components/scheduler/child/scheduler_message_loop_delegate.h index 05e405b2..2fce4b6 100644 --- a/content/child/scheduler/scheduler_message_loop_delegate.h +++ b/components/scheduler/child/scheduler_message_loop_delegate.h
@@ -2,16 +2,16 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CONTENT_CHILD_SCHEDULER_SCHEDULER_MESSAGE_LOOP_DELEGATE_H_ -#define CONTENT_CHILD_SCHEDULER_SCHEDULER_MESSAGE_LOOP_DELEGATE_H_ +#ifndef COMPONENTS_SCHEDULER_CHILD_SCHEDULER_MESSAGE_LOOP_DELEGATE_H_ +#define COMPONENTS_SCHEDULER_CHILD_SCHEDULER_MESSAGE_LOOP_DELEGATE_H_ #include "base/message_loop/message_loop.h" -#include "content/child/scheduler/nestable_single_thread_task_runner.h" -#include "content/common/content_export.h" +#include "components/scheduler/child/nestable_single_thread_task_runner.h" +#include "components/scheduler/scheduler_export.h" -namespace content { +namespace scheduler { -class CONTENT_EXPORT SchedulerMessageLoopDelegate +class SCHEDULER_EXPORT SchedulerMessageLoopDelegate : public NestableSingleThreadTaskRunner { public: // |message_loop| is not owned and must outlive the lifetime of this object. @@ -40,6 +40,6 @@ DISALLOW_COPY_AND_ASSIGN(SchedulerMessageLoopDelegate); }; -} // namespace content +} // namespace scheduler -#endif // CONTENT_CHILD_SCHEDULER_SCHEDULER_MESSAGE_LOOP_DELEGATE_H_ +#endif // COMPONENTS_SCHEDULER_CHILD_SCHEDULER_MESSAGE_LOOP_DELEGATE_H_
diff --git a/content/child/scheduler/single_thread_idle_task_runner.cc b/components/scheduler/child/single_thread_idle_task_runner.cc similarity index 86% rename from content/child/scheduler/single_thread_idle_task_runner.cc rename to components/scheduler/child/single_thread_idle_task_runner.cc index 1b0eb69..e7aeda3 100644 --- a/content/child/scheduler/single_thread_idle_task_runner.cc +++ b/components/scheduler/child/single_thread_idle_task_runner.cc
@@ -2,12 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "content/child/scheduler/single_thread_idle_task_runner.h" +#include "components/scheduler/child/single_thread_idle_task_runner.h" #include "base/location.h" #include "base/trace_event/trace_event.h" -namespace content { +namespace scheduler { SingleThreadIdleTaskRunner::SingleThreadIdleTaskRunner( scoped_refptr<base::SingleThreadTaskRunner> idle_priority_task_runner, @@ -37,18 +37,16 @@ const tracked_objects::Location& from_here, const IdleTask& idle_task) { idle_priority_task_runner_->PostTask( - from_here, - base::Bind(&SingleThreadIdleTaskRunner::RunTask, - weak_scheduler_ptr_, idle_task)); + from_here, base::Bind(&SingleThreadIdleTaskRunner::RunTask, + weak_scheduler_ptr_, idle_task)); } void SingleThreadIdleTaskRunner::PostNonNestableIdleTask( const tracked_objects::Location& from_here, const IdleTask& idle_task) { idle_priority_task_runner_->PostNonNestableTask( - from_here, - base::Bind(&SingleThreadIdleTaskRunner::RunTask, - weak_scheduler_ptr_, idle_task)); + from_here, base::Bind(&SingleThreadIdleTaskRunner::RunTask, + weak_scheduler_ptr_, idle_task)); } void SingleThreadIdleTaskRunner::PostIdleTaskAfterWakeup( @@ -75,4 +73,4 @@ } } -} // namespace content +} // namespace scheduler
diff --git a/content/child/scheduler/single_thread_idle_task_runner.h b/components/scheduler/child/single_thread_idle_task_runner.h similarity index 89% rename from content/child/scheduler/single_thread_idle_task_runner.h rename to components/scheduler/child/single_thread_idle_task_runner.h index b5281a57..358389a 100644 --- a/content/child/scheduler/single_thread_idle_task_runner.h +++ b/components/scheduler/child/single_thread_idle_task_runner.h
@@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CONTENT_CHILD_SCHEDULER_SINGLE_THREAD_IDLE_TASK_RUNNER_H_ -#define CONTENT_CHILD_SCHEDULER_SINGLE_THREAD_IDLE_TASK_RUNNER_H_ +#ifndef COMPONENTS_SCHEDULER_CHILD_SINGLE_THREAD_IDLE_TASK_RUNNER_H_ +#define COMPONENTS_SCHEDULER_CHILD_SINGLE_THREAD_IDLE_TASK_RUNNER_H_ #include "base/bind.h" #include "base/callback.h" @@ -11,7 +11,7 @@ #include "base/single_thread_task_runner.h" #include "base/time/time.h" -namespace content { +namespace scheduler { // A SingleThreadIdleTaskRunner is a task runner for running idle tasks. Idle // tasks have an unbound argument which is bound to a deadline @@ -60,6 +60,6 @@ DISALLOW_COPY_AND_ASSIGN(SingleThreadIdleTaskRunner); }; -} // namespace content +} // namespace scheduler -#endif // CONTENT_CHILD_SCHEDULER_SINGLE_THREAD_IDLE_TASK_RUNNER_H_ +#endif // COMPONENTS_SCHEDULER_CHILD_SINGLE_THREAD_IDLE_TASK_RUNNER_H_
diff --git a/content/child/scheduler/task_queue_manager.cc b/components/scheduler/child/task_queue_manager.cc similarity index 98% rename from content/child/scheduler/task_queue_manager.cc rename to components/scheduler/child/task_queue_manager.cc index 646c8591..2c03bd6 100644 --- a/content/child/scheduler/task_queue_manager.cc +++ b/components/scheduler/child/task_queue_manager.cc
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "content/child/scheduler/task_queue_manager.h" +#include "components/scheduler/child/task_queue_manager.h" #include <queue> #include <set> @@ -10,15 +10,15 @@ #include "base/bind.h" #include "base/trace_event/trace_event.h" #include "base/trace_event/trace_event_argument.h" -#include "content/child/scheduler/nestable_single_thread_task_runner.h" -#include "content/child/scheduler/task_queue_selector.h" -#include "content/child/scheduler/time_source.h" +#include "components/scheduler/child/nestable_single_thread_task_runner.h" +#include "components/scheduler/child/task_queue_selector.h" +#include "components/scheduler/child/time_source.h" namespace { const int64_t kMaxTimeTicks = std::numeric_limits<int64>::max(); } -namespace content { +namespace scheduler { namespace internal { // Now() is somewhat expensive so it makes sense not to call Now() unless we @@ -606,7 +606,7 @@ MaybePostDoWorkOnMainRunner(); if (ProcessTaskFromWorkQueue(queue_index, i > 0, &previous_task)) - return; // The TaskQueueManager got deleted, we must bail out. + return; // The TaskQueueManager got deleted, we must bail out. if (!UpdateWorkQueues(&previous_task)) return; @@ -741,4 +741,4 @@ MaybePostDoWorkOnMainRunner(); } -} // namespace content +} // namespace scheduler
diff --git a/content/child/scheduler/task_queue_manager.h b/components/scheduler/child/task_queue_manager.h similarity index 96% rename from content/child/scheduler/task_queue_manager.h rename to components/scheduler/child/task_queue_manager.h index 48588194..dad1573 100644 --- a/content/child/scheduler/task_queue_manager.h +++ b/components/scheduler/child/task_queue_manager.h
@@ -14,9 +14,9 @@ #include "base/single_thread_task_runner.h" #include "base/synchronization/lock.h" #include "base/threading/thread_checker.h" -#include "content/child/scheduler/task_queue_selector.h" -#include "content/child/scheduler/time_source.h" -#include "content/common/content_export.h" +#include "components/scheduler/child/task_queue_selector.h" +#include "components/scheduler/child/time_source.h" +#include "components/scheduler/scheduler_export.h" namespace base { namespace trace_event { @@ -25,7 +25,7 @@ } } -namespace content { +namespace scheduler { namespace internal { class LazyNow; class TaskQueue; @@ -46,8 +46,7 @@ // the incoming task queue (if any) are moved here. The work queues are // registered with the selector as input to the scheduling decision. // -class CONTENT_EXPORT TaskQueueManager - : public TaskQueueSelector::Observer { +class SCHEDULER_EXPORT TaskQueueManager : public TaskQueueSelector::Observer { public: // Keep TaskQueue::PumpPolicyToString in sync with this enum. enum class PumpPolicy { @@ -221,6 +220,6 @@ DISALLOW_COPY_AND_ASSIGN(TaskQueueManager); }; -} // namespace content +} // namespace scheduler #endif // CONTENT_RENDERER_SCHEDULER_TASK_QUEUE_MANAGER_H_
diff --git a/content/child/scheduler/task_queue_manager_perftest.cc b/components/scheduler/child/task_queue_manager_perftest.cc similarity index 95% rename from content/child/scheduler/task_queue_manager_perftest.cc rename to components/scheduler/child/task_queue_manager_perftest.cc index e056872..80a9c901 100644 --- a/content/child/scheduler/task_queue_manager_perftest.cc +++ b/components/scheduler/child/task_queue_manager_perftest.cc
@@ -2,16 +2,16 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "content/child/scheduler/task_queue_manager.h" +#include "components/scheduler/child/task_queue_manager.h" #include "base/bind.h" #include "base/threading/thread.h" -#include "content/child/scheduler/scheduler_message_loop_delegate.h" -#include "content/child/scheduler/task_queue_selector.h" +#include "components/scheduler/child/scheduler_message_loop_delegate.h" +#include "components/scheduler/child/task_queue_selector.h" #include "testing/gtest/include/gtest/gtest.h" #include "testing/perf/perf_test.h" -namespace content { +namespace scheduler { namespace { @@ -172,4 +172,4 @@ // TODO(alexclarke): Add additional tests with different mixes of non-delayed vs // delayed tasks. -} // namespace content +} // namespace scheduler
diff --git a/content/child/scheduler/task_queue_manager_unittest.cc b/components/scheduler/child/task_queue_manager_unittest.cc similarity index 98% rename from content/child/scheduler/task_queue_manager_unittest.cc rename to components/scheduler/child/task_queue_manager_unittest.cc index 5a573200..f34f878 100644 --- a/content/child/scheduler/task_queue_manager_unittest.cc +++ b/components/scheduler/child/task_queue_manager_unittest.cc
@@ -2,21 +2,21 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "content/child/scheduler/task_queue_manager.h" +#include "components/scheduler/child/task_queue_manager.h" #include "base/threading/thread.h" #include "cc/test/ordered_simple_task_runner.h" #include "cc/test/test_now_source.h" -#include "content/child/scheduler/nestable_task_runner_for_test.h" -#include "content/child/scheduler/scheduler_message_loop_delegate.h" -#include "content/child/scheduler/task_queue_selector.h" -#include "content/test/test_time_source.h" +#include "components/scheduler/child/nestable_task_runner_for_test.h" +#include "components/scheduler/child/scheduler_message_loop_delegate.h" +#include "components/scheduler/child/task_queue_selector.h" +#include "components/scheduler/child/test_time_source.h" #include "testing/gmock/include/gmock/gmock.h" using testing::ElementsAre; using testing::_; -namespace content { +namespace scheduler { namespace { class SelectorForTest : public TaskQueueSelector { @@ -59,8 +59,7 @@ return work_queues_; } - void SetTaskQueueSelectorObserver(Observer* observer) override { - } + void SetTaskQueueSelectorObserver(Observer* observer) override {} private: std::vector<const base::TaskQueue*> work_queues_; @@ -94,8 +93,7 @@ return work_queues_; } - void SetTaskQueueSelectorObserver(Observer* observer) override { - } + void SetTaskQueueSelectorObserver(Observer* observer) override {} private: std::deque<size_t> queues_to_service_; @@ -106,9 +104,7 @@ class TaskQueueManagerTest : public testing::Test { public: - void DeleteTaskQueueManager() { - manager_.reset(); - } + void DeleteTaskQueueManager() { manager_.reset(); } protected: enum class SelectorType { @@ -454,8 +450,8 @@ runner->PostDelayedTask( FROM_HERE, base::Bind(&TestObject::Run, base::Owned(new TestObject())), delay); - runner->PostTask( - FROM_HERE, base::Bind(&TestObject::Run, base::Owned(new TestObject()))); + runner->PostTask(FROM_HERE, + base::Bind(&TestObject::Run, base::Owned(new TestObject()))); manager_.reset(); @@ -1103,4 +1099,4 @@ } } // namespace -} // namespace content +} // namespace scheduler
diff --git a/content/child/scheduler/task_queue_selector.h b/components/scheduler/child/task_queue_selector.h similarity index 81% rename from content/child/scheduler/task_queue_selector.h rename to components/scheduler/child/task_queue_selector.h index 0a6c256..2af9e2b 100644 --- a/content/child/scheduler/task_queue_selector.h +++ b/components/scheduler/child/task_queue_selector.h
@@ -2,13 +2,13 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CONTENT_CHILD_SCHEDULER_TASK_QUEUE_SELECTOR_H_ -#define CONTENT_CHILD_SCHEDULER_TASK_QUEUE_SELECTOR_H_ +#ifndef COMPONENTS_SCHEDULER_CHILD_TASK_QUEUE_SELECTOR_H_ +#define COMPONENTS_SCHEDULER_CHILD_TASK_QUEUE_SELECTOR_H_ #include <vector> #include "base/basictypes.h" -#include "content/common/content_export.h" +#include "components/scheduler/scheduler_export.h" namespace base { class TaskQueue; @@ -17,7 +17,7 @@ } // namespace trace_event } // namespace base -namespace content { +namespace scheduler { class TaskQueueSelector { public: @@ -28,7 +28,7 @@ virtual void RegisterWorkQueues( const std::vector<const base::TaskQueue*>& work_queues) = 0; - class CONTENT_EXPORT Observer { + class SCHEDULER_EXPORT Observer { public: virtual ~Observer() {} @@ -51,6 +51,6 @@ virtual void AsValueInto(base::trace_event::TracedValue* state) const = 0; }; -} // namespace content +} // namespace scheduler -#endif // CONTENT_CHILD_SCHEDULER_TASK_QUEUE_SELECTOR_H_ +#endif // COMPONENTS_SCHEDULER_CHILD_TASK_QUEUE_SELECTOR_H_
diff --git a/content/test/test_time_source.cc b/components/scheduler/child/test_time_source.cc similarity index 80% rename from content/test/test_time_source.cc rename to components/scheduler/child/test_time_source.cc index ad602fd..3482bb5 100644 --- a/content/test/test_time_source.cc +++ b/components/scheduler/child/test_time_source.cc
@@ -2,11 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "content/test/test_time_source.h" +#include "components/scheduler/child/test_time_source.h" #include "cc/test/test_now_source.h" -namespace content { +namespace scheduler { TestTimeSource::TestTimeSource(scoped_refptr<cc::TestNowSource> time_source) : time_source_(time_source) { @@ -19,4 +19,4 @@ return time_source_->Now(); } -} // namespace content +} // namespace scheduler
diff --git a/content/test/test_time_source.h b/components/scheduler/child/test_time_source.h similarity index 67% rename from content/test/test_time_source.h rename to components/scheduler/child/test_time_source.h index 04270ec..6219a4dbd 100644 --- a/content/test/test_time_source.h +++ b/components/scheduler/child/test_time_source.h
@@ -2,17 +2,17 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CONTENT_TEST_TEST_TIME_SOURCE_H_ -#define CONTENT_TEST_TEST_TIME_SOURCE_H_ +#ifndef COMPONENTS_SCHEDULER_CHILD_TEST_TIME_SOURCE_H_ +#define COMPONENTS_SCHEDULER_CHILD_TEST_TIME_SOURCE_H_ #include "base/memory/ref_counted.h" -#include "content/child/scheduler/time_source.h" +#include "components/scheduler/child/time_source.h" namespace cc { class TestNowSource; } -namespace content { +namespace scheduler { class TestTimeSource : public TimeSource { public: @@ -27,6 +27,6 @@ DISALLOW_COPY_AND_ASSIGN(TestTimeSource); }; -} // namespace content +} // namespace scheduler -#endif // CONTENT_TEST_TEST_TIME_SOURCE_H_ +#endif // COMPONENTS_SCHEDULER_CHILD_TEST_TIME_SOURCE_H_
diff --git a/content/child/scheduler/time_source.cc b/components/scheduler/child/time_source.cc similarity index 75% rename from content/child/scheduler/time_source.cc rename to components/scheduler/child/time_source.cc index ce64df8..f8cf6c43 100644 --- a/content/child/scheduler/time_source.cc +++ b/components/scheduler/child/time_source.cc
@@ -2,9 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "content/child/scheduler/time_source.h" +#include "components/scheduler/child/time_source.h" -namespace content { +namespace scheduler { TimeSource::TimeSource() { } @@ -16,4 +16,4 @@ return base::TimeTicks::Now(); } -} // namespace content +} // namespace scheduler
diff --git a/components/scheduler/child/time_source.h b/components/scheduler/child/time_source.h new file mode 100644 index 0000000..079e78dc --- /dev/null +++ b/components/scheduler/child/time_source.h
@@ -0,0 +1,26 @@ +// 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 COMPONENTS_SCHEDULER_CHILD_TIME_SOURCE_H_ +#define COMPONENTS_SCHEDULER_CHILD_TIME_SOURCE_H_ + +#include "base/time/time.h" +#include "components/scheduler/scheduler_export.h" + +namespace scheduler { + +class SCHEDULER_EXPORT TimeSource { + public: + TimeSource(); + virtual ~TimeSource(); + + virtual base::TimeTicks Now() const; + + private: + DISALLOW_COPY_AND_ASSIGN(TimeSource); +}; + +} // namespace scheduler + +#endif // COMPONENTS_SCHEDULER_CHILD_TIME_SOURCE_H_
diff --git a/content/child/scheduler/web_scheduler_impl.cc b/components/scheduler/child/web_scheduler_impl.cc similarity index 93% rename from content/child/scheduler/web_scheduler_impl.cc rename to components/scheduler/child/web_scheduler_impl.cc index 25772f8..9d8aac9 100644 --- a/content/child/scheduler/web_scheduler_impl.cc +++ b/components/scheduler/child/web_scheduler_impl.cc
@@ -2,14 +2,14 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "content/child/scheduler/web_scheduler_impl.h" +#include "components/scheduler/child/web_scheduler_impl.h" #include "base/bind.h" #include "base/single_thread_task_runner.h" -#include "content/child/scheduler/worker_scheduler.h" +#include "components/scheduler/child/worker_scheduler.h" #include "third_party/WebKit/public/platform/WebTraceLocation.h" -namespace content { +namespace scheduler { WebSchedulerImpl::WebSchedulerImpl( ChildScheduler* child_scheduler, @@ -97,10 +97,11 @@ scoped_ptr<blink::WebThread::Task> scoped_task(task); tracked_objects::Location location(web_location.functionName(), web_location.fileName(), -1, nullptr); - timer_task_runner_->PostDelayedTask( + // Timer tasks should not run in a nested messageloop. + timer_task_runner_->PostNonNestableDelayedTask( location, base::Bind(&WebSchedulerImpl::runTask, base::Passed(&scoped_task)), base::TimeDelta::FromMilliseconds(delayMs)); } -} // namespace content +} // namespace scheduler
diff --git a/content/child/scheduler/web_scheduler_impl.h b/components/scheduler/child/web_scheduler_impl.h similarity index 92% rename from content/child/scheduler/web_scheduler_impl.h rename to components/scheduler/child/web_scheduler_impl.h index 0f661b4..0d1b484 100644 --- a/content/child/scheduler/web_scheduler_impl.h +++ b/components/scheduler/child/web_scheduler_impl.h
@@ -8,7 +8,7 @@ #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "base/time/time.h" -#include "content/common/content_export.h" +#include "components/scheduler/scheduler_export.h" #include "third_party/WebKit/public/platform/WebScheduler.h" #include "third_party/WebKit/public/platform/WebThread.h" @@ -16,12 +16,12 @@ class SingleThreadTaskRunner; } -namespace content { +namespace scheduler { class ChildScheduler; class SingleThreadIdleTaskRunner; -class CONTENT_EXPORT WebSchedulerImpl : public blink::WebScheduler { +class SCHEDULER_EXPORT WebSchedulerImpl : public blink::WebScheduler { public: WebSchedulerImpl( ChildScheduler* child_scheduler, @@ -56,6 +56,6 @@ scoped_refptr<base::SingleThreadTaskRunner> timer_task_runner_; }; -} // namespace content +} // namespace scheduler #endif // CONTENT_CHILD_SCHEDULER_BASE_WEB_SCHEDULER_IMPL_H_
diff --git a/content/child/webthread_base.cc b/components/scheduler/child/webthread_base.cc similarity index 96% rename from content/child/webthread_base.cc rename to components/scheduler/child/webthread_base.cc index 818c960..9da4254a 100644 --- a/content/child/webthread_base.cc +++ b/components/scheduler/child/webthread_base.cc
@@ -5,16 +5,16 @@ // An implementation of WebThread in terms of base::MessageLoop and // base::Thread -#include "content/child/webthread_base.h" +#include "components/scheduler/child/webthread_base.h" #include "base/bind.h" #include "base/bind_helpers.h" #include "base/pending_task.h" #include "base/threading/platform_thread.h" -#include "content/child/scheduler/single_thread_idle_task_runner.h" +#include "components/scheduler/child/single_thread_idle_task_runner.h" #include "third_party/WebKit/public/platform/WebTraceLocation.h" -namespace content { +namespace scheduler { class WebThreadBase::TaskObserverAdapter : public base::MessageLoop::TaskObserver { @@ -161,4 +161,4 @@ return TaskRunner()->BelongsToCurrentThread(); } -} // namespace content +} // namespace scheduler
diff --git a/content/child/webthread_base.h b/components/scheduler/child/webthread_base.h similarity index 85% rename from content/child/webthread_base.h rename to components/scheduler/child/webthread_base.h index 5d6ebb9b..c7f86562 100644 --- a/content/child/webthread_base.h +++ b/components/scheduler/child/webthread_base.h
@@ -2,24 +2,24 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CONTENT_CHILD_WEBTHREAD_BASE_H_ -#define CONTENT_CHILD_WEBTHREAD_BASE_H_ +#ifndef COMPONENTS_SCHEDULER_CHILD_WEBTHREAD_BASE_H_ +#define COMPONENTS_SCHEDULER_CHILD_WEBTHREAD_BASE_H_ #include <map> #include "base/memory/scoped_ptr.h" #include "base/threading/thread.h" -#include "content/common/content_export.h" +#include "components/scheduler/scheduler_export.h" #include "third_party/WebKit/public/platform/WebThread.h" namespace blink { class WebTraceLocation; } -namespace content { +namespace scheduler { class SingleThreadIdleTaskRunner; -class CONTENT_EXPORT WebThreadBase : public blink::WebThread { +class SCHEDULER_EXPORT WebThreadBase : public blink::WebThread { public: virtual ~WebThreadBase(); @@ -48,7 +48,7 @@ // Returns the base::Bind-compatible task runner for posting idle tasks to // this thread. Can be called from any thread. - virtual SingleThreadIdleTaskRunner* IdleTaskRunner() const = 0; + virtual scheduler::SingleThreadIdleTaskRunner* IdleTaskRunner() const = 0; protected: class TaskObserverAdapter; @@ -75,6 +75,6 @@ TaskObserverMap task_observer_map_; }; -} // namespace content +} // namespace scheduler -#endif // CONTENT_CHILD_WEBTHREAD_BASE_H_ +#endif // COMPONENTS_SCHEDULER_CHILD_WEBTHREAD_BASE_H_
diff --git a/content/child/scheduler/webthread_impl_for_worker_scheduler.cc b/components/scheduler/child/webthread_impl_for_worker_scheduler.cc similarity index 89% rename from content/child/scheduler/webthread_impl_for_worker_scheduler.cc rename to components/scheduler/child/webthread_impl_for_worker_scheduler.cc index 09004f8..c293faae 100644 --- a/content/child/scheduler/webthread_impl_for_worker_scheduler.cc +++ b/components/scheduler/child/webthread_impl_for_worker_scheduler.cc
@@ -2,16 +2,16 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "content/child/scheduler/webthread_impl_for_worker_scheduler.h" +#include "components/scheduler/child/webthread_impl_for_worker_scheduler.h" #include "base/bind.h" #include "base/synchronization/waitable_event.h" -#include "content/child/scheduler/scheduler_message_loop_delegate.h" -#include "content/child/scheduler/web_scheduler_impl.h" -#include "content/child/scheduler/worker_scheduler_impl.h" +#include "components/scheduler/child/scheduler_message_loop_delegate.h" +#include "components/scheduler/child/web_scheduler_impl.h" +#include "components/scheduler/child/worker_scheduler_impl.h" #include "third_party/WebKit/public/platform/WebTraceLocation.h" -namespace content { +namespace scheduler { WebThreadImplForWorkerScheduler::WebThreadImplForWorkerScheduler( const char* name) @@ -92,4 +92,4 @@ worker_scheduler_->RemoveTaskObserver(observer); } -} // namespace content +} // namespace scheduler
diff --git a/content/child/scheduler/webthread_impl_for_worker_scheduler.h b/components/scheduler/child/webthread_impl_for_worker_scheduler.h similarity index 61% rename from content/child/scheduler/webthread_impl_for_worker_scheduler.h rename to components/scheduler/child/webthread_impl_for_worker_scheduler.h index d5346ba..2ea02e4 100644 --- a/content/child/scheduler/webthread_impl_for_worker_scheduler.h +++ b/components/scheduler/child/webthread_impl_for_worker_scheduler.h
@@ -2,11 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CONTENT_CHILD_SCHEDULER_WEBTHREAD_IMPL_FOR_WORKER_SCHEDULER_H_ -#define CONTENT_CHILD_SCHEDULER_WEBTHREAD_IMPL_FOR_WORKER_SCHEDULER_H_ +#ifndef COMPONENTS_SCHEDULER_CHILD_WEBTHREAD_IMPL_FOR_WORKER_SCHEDULER_H_ +#define COMPONENTS_SCHEDULER_CHILD_WEBTHREAD_IMPL_FOR_WORKER_SCHEDULER_H_ -#include "content/child/scheduler/task_queue_manager.h" -#include "content/child/webthread_base.h" +#include "components/scheduler/child/task_queue_manager.h" +#include "components/scheduler/child/webthread_base.h" namespace base { class WaitableEvent; @@ -16,13 +16,12 @@ class WebScheduler; }; -namespace content { - +namespace scheduler { class SingleThreadIdleTaskRunner; class WebSchedulerImpl; class WorkerScheduler; -class CONTENT_EXPORT WebThreadImplForWorkerScheduler : public WebThreadBase { +class SCHEDULER_EXPORT WebThreadImplForWorkerScheduler : public WebThreadBase { public: explicit WebThreadImplForWorkerScheduler(const char* name); virtual ~WebThreadImplForWorkerScheduler(); @@ -33,7 +32,7 @@ // WebThreadBase implementation. base::SingleThreadTaskRunner* TaskRunner() const override; - SingleThreadIdleTaskRunner* IdleTaskRunner() const override; + scheduler::SingleThreadIdleTaskRunner* IdleTaskRunner() const override; private: base::MessageLoop* MessageLoop() const override; @@ -46,12 +45,12 @@ void ShutDownOnThread(base::WaitableEvent* completion); scoped_ptr<base::Thread> thread_; - scoped_ptr<WorkerScheduler> worker_scheduler_; - scoped_ptr<WebSchedulerImpl> web_scheduler_; + scoped_ptr<scheduler::WorkerScheduler> worker_scheduler_; + scoped_ptr<scheduler::WebSchedulerImpl> web_scheduler_; scoped_refptr<base::SingleThreadTaskRunner> task_runner_; - scoped_refptr<SingleThreadIdleTaskRunner> idle_task_runner_; + scoped_refptr<scheduler::SingleThreadIdleTaskRunner> idle_task_runner_; }; -} // namespace content +} // namespace scheduler -#endif // CONTENT_CHILD_SCHEDULER_WEBTHREAD_IMPL_FOR_WORKER_SCHEDULER_H_ +#endif // COMPONENTS_SCHEDULER_CHILD_WEBTHREAD_IMPL_FOR_WORKER_SCHEDULER_H_
diff --git a/content/child/scheduler/webthread_impl_for_worker_scheduler_unittest.cc b/components/scheduler/child/webthread_impl_for_worker_scheduler_unittest.cc similarity index 95% rename from content/child/scheduler/webthread_impl_for_worker_scheduler_unittest.cc rename to components/scheduler/child/webthread_impl_for_worker_scheduler_unittest.cc index b03749a..7bb1f89 100644 --- a/content/child/scheduler/webthread_impl_for_worker_scheduler_unittest.cc +++ b/components/scheduler/child/webthread_impl_for_worker_scheduler_unittest.cc
@@ -2,10 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "content/child/scheduler/webthread_impl_for_worker_scheduler.h" +#include "components/scheduler/child/webthread_impl_for_worker_scheduler.h" #include "base/synchronization/waitable_event.h" -#include "content/child/scheduler/worker_scheduler_impl.h" +#include "components/scheduler/child/worker_scheduler_impl.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/WebKit/public/platform/WebTraceLocation.h" @@ -15,7 +15,7 @@ using testing::ElementsAre; using testing::Invoke; -namespace content { +namespace scheduler { namespace { class NopTask : public blink::WebThread::Task { @@ -167,4 +167,4 @@ EXPECT_THAT(calls, testing::HasSubstr("willProcessTask run didProcessTask")); } -} // namespace content +} // namespace scheduler
diff --git a/content/child/scheduler/worker_scheduler.cc b/components/scheduler/child/worker_scheduler.cc similarity index 67% rename from content/child/scheduler/worker_scheduler.cc rename to components/scheduler/child/worker_scheduler.cc index ac30ab3..a1ddc988 100644 --- a/content/child/scheduler/worker_scheduler.cc +++ b/components/scheduler/child/worker_scheduler.cc
@@ -2,16 +2,16 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "content/child/scheduler/worker_scheduler.h" +#include "components/scheduler/child/worker_scheduler.h" #include "base/command_line.h" #include "base/message_loop/message_loop.h" -#include "content/child/scheduler/null_worker_scheduler.h" -#include "content/child/scheduler/scheduler_message_loop_delegate.h" -#include "content/child/scheduler/worker_scheduler_impl.h" -#include "content/public/common/content_switches.h" +#include "components/scheduler/child/null_worker_scheduler.h" +#include "components/scheduler/child/scheduler_message_loop_delegate.h" +#include "components/scheduler/child/worker_scheduler_impl.h" +#include "components/scheduler/common/scheduler_switches.h" -namespace content { +namespace scheduler { WorkerScheduler::WorkerScheduler() { } @@ -31,4 +31,4 @@ } } -} // namespace content +} // namespace scheduler
diff --git a/components/scheduler/child/worker_scheduler.h b/components/scheduler/child/worker_scheduler.h new file mode 100644 index 0000000..33382109 --- /dev/null +++ b/components/scheduler/child/worker_scheduler.h
@@ -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. + +#ifndef COMPONENTS_SCHEDULER_CHILD_WORKER_SCHEDULER_H_ +#define COMPONENTS_SCHEDULER_CHILD_WORKER_SCHEDULER_H_ + +#include "base/message_loop/message_loop.h" +#include "components/scheduler/child/child_scheduler.h" +#include "components/scheduler/child/single_thread_idle_task_runner.h" +#include "components/scheduler/scheduler_export.h" + +namespace base { +class MessageLoop; +} + +namespace scheduler { + +class SCHEDULER_EXPORT WorkerScheduler : public ChildScheduler { + public: + ~WorkerScheduler() override; + static scoped_ptr<WorkerScheduler> Create(base::MessageLoop* message_loop); + + // Must be called before the scheduler can be used. Does any post construction + // initialization needed such as initializing idle period detection. + virtual void Init() = 0; + + protected: + WorkerScheduler(); + DISALLOW_COPY_AND_ASSIGN(WorkerScheduler); +}; + +} // namespace scheduler + +#endif // COMPONENTS_SCHEDULER_CHILD_WORKER_SCHEDULER_H_
diff --git a/content/child/scheduler/worker_scheduler_impl.cc b/components/scheduler/child/worker_scheduler_impl.cc similarity index 88% rename from content/child/scheduler/worker_scheduler_impl.cc rename to components/scheduler/child/worker_scheduler_impl.cc index c22c0c3..cfd444e9 100644 --- a/content/child/scheduler/worker_scheduler_impl.cc +++ b/components/scheduler/child/worker_scheduler_impl.cc
@@ -2,15 +2,15 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "content/child/scheduler/worker_scheduler_impl.h" +#include "components/scheduler/child/worker_scheduler_impl.h" #include "base/bind.h" #include "base/message_loop/message_loop_proxy.h" #include "base/trace_event/trace_event.h" #include "base/trace_event/trace_event_argument.h" -#include "content/child/scheduler/nestable_single_thread_task_runner.h" +#include "components/scheduler/child/nestable_single_thread_task_runner.h" -namespace content { +namespace scheduler { WorkerSchedulerImpl::WorkerSchedulerImpl( scoped_refptr<NestableSingleThreadTaskRunner> main_task_runner) @@ -79,10 +79,6 @@ return &helper_; } -void WorkerSchedulerImpl::SetWorkBatchSizeForTesting(size_t work_batch_size) { - helper_.SetWorkBatchSizeForTesting(work_batch_size); -} - bool WorkerSchedulerImpl::CanEnterLongIdlePeriod(base::TimeTicks, base::TimeDelta*) { return true; @@ -94,4 +90,4 @@ return deadline; } -} // namespace content +} // namespace scheduler
diff --git a/content/child/scheduler/worker_scheduler_impl.h b/components/scheduler/child/worker_scheduler_impl.h similarity index 77% rename from content/child/scheduler/worker_scheduler_impl.h rename to components/scheduler/child/worker_scheduler_impl.h index 06e9d58..989d9cf 100644 --- a/content/child/scheduler/worker_scheduler_impl.h +++ b/components/scheduler/child/worker_scheduler_impl.h
@@ -2,11 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CONTENT_CHILD_SCHEDULER_WORKER_SCHEDULER_IMPL_H_ -#define CONTENT_CHILD_SCHEDULER_WORKER_SCHEDULER_IMPL_H_ +#ifndef COMPONENTS_SCHEDULER_CHILD_WORKER_SCHEDULER_IMPL_H_ +#define COMPONENTS_SCHEDULER_CHILD_WORKER_SCHEDULER_IMPL_H_ -#include "content/child/scheduler/worker_scheduler.h" -#include "content/child/scheduler/scheduler_helper.h" +#include "components/scheduler/child/scheduler_helper.h" +#include "components/scheduler/child/worker_scheduler.h" +#include "components/scheduler/scheduler_export.h" namespace base { namespace trace_event { @@ -14,11 +15,11 @@ } } -namespace content { +namespace scheduler { class NestableSingleThreadTaskRunner; -class CONTENT_EXPORT WorkerSchedulerImpl +class SCHEDULER_EXPORT WorkerSchedulerImpl : public WorkerScheduler, public SchedulerHelper::SchedulerHelperDelegate { public: @@ -38,7 +39,6 @@ void Shutdown() override; SchedulerHelper* GetSchedulerHelperForTesting(); - void SetWorkBatchSizeForTesting(size_t work_batch_size); base::TimeTicks CurrentIdleTaskDeadlineForTesting() const; protected: @@ -57,6 +57,6 @@ DISALLOW_COPY_AND_ASSIGN(WorkerSchedulerImpl); }; -} // namespace content +} // namespace scheduler -#endif // CONTENT_CHILD_SCHEDULER_WORKER_SCHEDULER_IMPL_H_ +#endif // COMPONENTS_SCHEDULER_CHILD_WORKER_SCHEDULER_IMPL_H_
diff --git a/content/child/scheduler/worker_scheduler_impl_unittest.cc b/components/scheduler/child/worker_scheduler_impl_unittest.cc similarity index 95% rename from content/child/scheduler/worker_scheduler_impl_unittest.cc rename to components/scheduler/child/worker_scheduler_impl_unittest.cc index edeaa36..495e88b6 100644 --- a/content/child/scheduler/worker_scheduler_impl_unittest.cc +++ b/components/scheduler/child/worker_scheduler_impl_unittest.cc
@@ -2,21 +2,21 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "content/child/scheduler/worker_scheduler_impl.h" +#include "components/scheduler/child/worker_scheduler_impl.h" #include "base/callback.h" #include "base/strings/stringprintf.h" #include "cc/test/ordered_simple_task_runner.h" #include "cc/test/test_now_source.h" -#include "content/child/scheduler/nestable_task_runner_for_test.h" -#include "content/child/scheduler/scheduler_message_loop_delegate.h" -#include "content/test/test_time_source.h" +#include "components/scheduler/child/nestable_task_runner_for_test.h" +#include "components/scheduler/child/scheduler_message_loop_delegate.h" +#include "components/scheduler/child/test_time_source.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" using testing::ElementsAreArray; -namespace content { +namespace scheduler { namespace { void NopTask() { @@ -28,15 +28,15 @@ void WakeUpTask(std::vector<std::string>* timeline, cc::TestNowSource* clock) { if (timeline) { - timeline->push_back(base::StringPrintf( - "run WakeUpTask @ %d", TimeTicksToIntMs(clock->Now()))); + timeline->push_back(base::StringPrintf("run WakeUpTask @ %d", + TimeTicksToIntMs(clock->Now()))); } } void RecordTimelineTask(std::vector<std::string>* timeline, cc::TestNowSource* clock) { - timeline->push_back(base::StringPrintf( - "run RecordTimelineTask @ %d", TimeTicksToIntMs(clock->Now()))); + timeline->push_back(base::StringPrintf("run RecordTimelineTask @ %d", + TimeTicksToIntMs(clock->Now()))); } void AppendToVectorTestTask(std::vector<std::string>* vector, @@ -423,4 +423,4 @@ EXPECT_THAT(timeline, ElementsAreArray(expected_timeline)); } -} // namespace content +} // namespace scheduler
diff --git a/components/scheduler/common/scheduler_switches.cc b/components/scheduler/common/scheduler_switches.cc new file mode 100644 index 0000000..7d7eca64a --- /dev/null +++ b/components/scheduler/common/scheduler_switches.cc
@@ -0,0 +1,15 @@ +// 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 "components/scheduler/common/scheduler_switches.h" + +namespace scheduler { +namespace switches { + +// Disable the Blink Scheduler. Ensures there's no reordering of blink tasks. +// This switch is intended only for performance tests. +const char kDisableBlinkScheduler[] = "disable-blink-scheduler"; + +} // namespace switches +} // namespace scheduler
diff --git a/components/scheduler/common/scheduler_switches.h b/components/scheduler/common/scheduler_switches.h new file mode 100644 index 0000000..8d909d9 --- /dev/null +++ b/components/scheduler/common/scheduler_switches.h
@@ -0,0 +1,16 @@ +// 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 COMPONENTS_SCHEDULER_COMMON_SCHEDULER_SWITCHES_H_ +#define COMPONENTS_SCHEDULER_COMMON_SCHEDULER_SWITCHES_H_ + +namespace scheduler { +namespace switches { + +extern const char kDisableBlinkScheduler[]; + +} // namespace switches +} // namespace scheduler + +#endif // COMPONENTS_SCHEDULER_COMMON_SCHEDULER_SWITCHES_H_
diff --git a/components/scheduler/renderer/DEPS b/components/scheduler/renderer/DEPS new file mode 100644 index 0000000..3425c1a --- /dev/null +++ b/components/scheduler/renderer/DEPS
@@ -0,0 +1,15 @@ +include_rules = [ + "+components/scheduler/child", + "+components/scheduler/common", + "+components/scheduler/scheduler_export.h", + "+cc/output", + "+ui/gfx", + "+third_party/WebKit/public/platform", + "+third_party/WebKit/public/web", +] + +specific_include_rules = { + ".*test\.cc": [ + "+cc/test", + ], +}
diff --git a/content/renderer/scheduler/deadline_task_runner.cc b/components/scheduler/renderer/deadline_task_runner.cc similarity index 90% rename from content/renderer/scheduler/deadline_task_runner.cc rename to components/scheduler/renderer/deadline_task_runner.cc index 04564cd..fd6859f 100644 --- a/content/renderer/scheduler/deadline_task_runner.cc +++ b/components/scheduler/renderer/deadline_task_runner.cc
@@ -2,11 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "content/renderer/scheduler/deadline_task_runner.h" +#include "components/scheduler/renderer/deadline_task_runner.h" #include "base/bind.h" -namespace content { +namespace scheduler { DeadlineTaskRunner::DeadlineTaskRunner( const base::Closure& callback, @@ -37,4 +37,4 @@ callback_.Run(); } -} // namespace content +} // namespace scheduler
diff --git a/content/renderer/scheduler/deadline_task_runner.h b/components/scheduler/renderer/deadline_task_runner.h similarity index 76% rename from content/renderer/scheduler/deadline_task_runner.h rename to components/scheduler/renderer/deadline_task_runner.h index e9b2755d..7988cc4 100644 --- a/content/renderer/scheduler/deadline_task_runner.h +++ b/components/scheduler/renderer/deadline_task_runner.h
@@ -2,21 +2,21 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CONTENT_RENDERER_SCHEDULER_DEADLINE_TASK_RUNNER_H_ -#define CONTENT_RENDERER_SCHEDULER_DEADLINE_TASK_RUNNER_H_ +#ifndef COMPONENTS_SCHEDULER_RENDERER_DEADLINE_TASK_RUNNER_H_ +#define COMPONENTS_SCHEDULER_RENDERER_DEADLINE_TASK_RUNNER_H_ #include "base/callback.h" #include "base/macros.h" #include "base/memory/weak_ptr.h" #include "base/single_thread_task_runner.h" #include "base/time/time.h" -#include "content/child/scheduler/cancelable_closure_holder.h" -#include "content/common/content_export.h" +#include "components/scheduler/child/cancelable_closure_holder.h" +#include "components/scheduler/scheduler_export.h" -namespace content { +namespace scheduler { // Runs a posted task at latest by a given deadline, but possibly sooner. -class CONTENT_EXPORT DeadlineTaskRunner { +class SCHEDULER_EXPORT DeadlineTaskRunner { public: DeadlineTaskRunner(const base::Closure& callback, scoped_refptr<base::SingleThreadTaskRunner> task_runner); @@ -45,6 +45,6 @@ DISALLOW_COPY_AND_ASSIGN(DeadlineTaskRunner); }; -} // namespace content +} // namespace scheduler -#endif // CONTENT_RENDERER_SCHEDULER_DEADLINE_TASK_RUNNER_H_ +#endif // COMPONENTS_SCHEDULER_RENDERER_DEADLINE_TASK_RUNNER_H_
diff --git a/content/renderer/scheduler/deadline_task_runner_unittest.cc b/components/scheduler/renderer/deadline_task_runner_unittest.cc similarity index 96% rename from content/renderer/scheduler/deadline_task_runner_unittest.cc rename to components/scheduler/renderer/deadline_task_runner_unittest.cc index 87ec6a1..ae56f41 100644 --- a/content/renderer/scheduler/deadline_task_runner_unittest.cc +++ b/components/scheduler/renderer/deadline_task_runner_unittest.cc
@@ -2,14 +2,14 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "content/renderer/scheduler/deadline_task_runner.h" +#include "components/scheduler/renderer/deadline_task_runner.h" #include "cc/test/ordered_simple_task_runner.h" #include "cc/test/test_now_source.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" -namespace content { +namespace scheduler { class DeadlineTaskRunnerTest : public testing::Test { public: @@ -95,4 +95,4 @@ EXPECT_TRUE(run_times_.empty()); }; -} // namespace content +} // namespace scheduler
diff --git a/content/renderer/scheduler/null_renderer_scheduler.cc b/components/scheduler/renderer/null_renderer_scheduler.cc similarity index 86% rename from content/renderer/scheduler/null_renderer_scheduler.cc rename to components/scheduler/renderer/null_renderer_scheduler.cc index 349ab54cb..22736b6 100644 --- a/content/renderer/scheduler/null_renderer_scheduler.cc +++ b/components/scheduler/renderer/null_renderer_scheduler.cc
@@ -2,16 +2,17 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "content/renderer/scheduler/null_renderer_scheduler.h" +#include "components/scheduler/renderer/null_renderer_scheduler.h" #include "base/bind.h" -#include "base/message_loop/message_loop_proxy.h" -#include "content/child/scheduler/null_idle_task_runner.h" +#include "base/message_loop/message_loop.h" +#include "base/thread_task_runner_handle.h" +#include "components/scheduler/child/null_idle_task_runner.h" -namespace content { +namespace scheduler { NullRendererScheduler::NullRendererScheduler() - : task_runner_(base::MessageLoopProxy::current()), + : task_runner_(base::ThreadTaskRunnerHandle::Get()), idle_task_runner_(new NullIdleTaskRunner()) { } @@ -96,4 +97,4 @@ void NullRendererScheduler::ResumeTimerQueue() { } -} // namespace content +} // namespace scheduler
diff --git a/content/renderer/scheduler/null_renderer_scheduler.h b/components/scheduler/renderer/null_renderer_scheduler.h similarity index 84% rename from content/renderer/scheduler/null_renderer_scheduler.h rename to components/scheduler/renderer/null_renderer_scheduler.h index 162e8d99..a346665 100644 --- a/content/renderer/scheduler/null_renderer_scheduler.h +++ b/components/scheduler/renderer/null_renderer_scheduler.h
@@ -2,12 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CONTENT_RENDERER_SCHEDULER_NULL_RENDERER_SCHEDULER_H_ -#define CONTENT_RENDERER_SCHEDULER_NULL_RENDERER_SCHEDULER_H_ +#ifndef COMPONENTS_SCHEDULER_RENDERER_NULL_RENDERER_SCHEDULER_H_ +#define COMPONENTS_SCHEDULER_RENDERER_NULL_RENDERER_SCHEDULER_H_ -#include "content/renderer/scheduler/renderer_scheduler.h" +#include "components/scheduler/renderer/renderer_scheduler.h" -namespace content { +namespace scheduler { class NullRendererScheduler : public RendererScheduler { public: @@ -45,6 +45,6 @@ DISALLOW_COPY_AND_ASSIGN(NullRendererScheduler); }; -} // namespace content +} // namespace scheduler -#endif // CONTENT_RENDERER_SCHEDULER_NULL_RENDERER_SCHEDULER_H_ +#endif // COMPONENTS_SCHEDULER_RENDERER_NULL_RENDERER_SCHEDULER_H_
diff --git a/content/renderer/scheduler/renderer_scheduler.cc b/components/scheduler/renderer/renderer_scheduler.cc similarity index 73% rename from content/renderer/scheduler/renderer_scheduler.cc rename to components/scheduler/renderer/renderer_scheduler.cc index 0ecba8c..95669507e 100644 --- a/content/renderer/scheduler/renderer_scheduler.cc +++ b/components/scheduler/renderer/renderer_scheduler.cc
@@ -2,18 +2,18 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "content/renderer/scheduler/renderer_scheduler.h" +#include "components/scheduler/renderer/renderer_scheduler.h" #include "base/command_line.h" #include "base/message_loop/message_loop.h" #include "base/trace_event/trace_event.h" #include "base/trace_event/trace_event_impl.h" -#include "content/child/scheduler/scheduler_message_loop_delegate.h" -#include "content/public/common/content_switches.h" -#include "content/renderer/scheduler/null_renderer_scheduler.h" -#include "content/renderer/scheduler/renderer_scheduler_impl.h" +#include "components/scheduler/child/scheduler_message_loop_delegate.h" +#include "components/scheduler/common/scheduler_switches.h" +#include "components/scheduler/renderer/null_renderer_scheduler.h" +#include "components/scheduler/renderer/renderer_scheduler_impl.h" -namespace content { +namespace scheduler { RendererScheduler::RendererScheduler() { } @@ -37,4 +37,4 @@ } } -} // namespace content +} // namespace scheduler
diff --git a/content/renderer/scheduler/renderer_scheduler.h b/components/scheduler/renderer/renderer_scheduler.h similarity index 86% rename from content/renderer/scheduler/renderer_scheduler.h rename to components/scheduler/renderer/renderer_scheduler.h index 7d08f31..e2756149 100644 --- a/content/renderer/scheduler/renderer_scheduler.h +++ b/components/scheduler/renderer/renderer_scheduler.h
@@ -2,22 +2,22 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CONTENT_RENDERER_SCHEDULER_RENDERER_SCHEDULER_H_ -#define CONTENT_RENDERER_SCHEDULER_RENDERER_SCHEDULER_H_ +#ifndef COMPONENTS_SCHEDULER_RENDERER_RENDERER_SCHEDULER_H_ +#define COMPONENTS_SCHEDULER_RENDERER_RENDERER_SCHEDULER_H_ #include "base/message_loop/message_loop.h" -#include "content/child/scheduler/child_scheduler.h" -#include "content/child/scheduler/single_thread_idle_task_runner.h" -#include "content/common/content_export.h" +#include "components/scheduler/child/child_scheduler.h" +#include "components/scheduler/child/single_thread_idle_task_runner.h" +#include "components/scheduler/scheduler_export.h" #include "third_party/WebKit/public/web/WebInputEvent.h" namespace cc { struct BeginFrameArgs; } -namespace content { +namespace scheduler { -class CONTENT_EXPORT RendererScheduler : public ChildScheduler { +class SCHEDULER_EXPORT RendererScheduler : public ChildScheduler { public: ~RendererScheduler() override; static scoped_ptr<RendererScheduler> Create(); @@ -84,6 +84,6 @@ DISALLOW_COPY_AND_ASSIGN(RendererScheduler); }; -} // namespace content +} // namespace scheduler -#endif // CONTENT_RENDERER_SCHEDULER_RENDERER_SCHEDULER_H_ +#endif // COMPONENTS_SCHEDULER_RENDERER_RENDERER_SCHEDULER_H_
diff --git a/content/renderer/scheduler/renderer_scheduler_impl.cc b/components/scheduler/renderer/renderer_scheduler_impl.cc similarity index 95% rename from content/renderer/scheduler/renderer_scheduler_impl.cc rename to components/scheduler/renderer/renderer_scheduler_impl.cc index 7ca3eb7a..5ed3d6d 100644 --- a/content/renderer/scheduler/renderer_scheduler_impl.cc +++ b/components/scheduler/renderer/renderer_scheduler_impl.cc
@@ -2,18 +2,18 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "content/renderer/scheduler/renderer_scheduler_impl.h" +#include "components/scheduler/renderer/renderer_scheduler_impl.h" #include "base/bind.h" #include "base/message_loop/message_loop_proxy.h" #include "base/trace_event/trace_event.h" #include "base/trace_event/trace_event_argument.h" #include "cc/output/begin_frame_args.h" -#include "content/child/scheduler/nestable_single_thread_task_runner.h" -#include "content/child/scheduler/prioritizing_task_queue_selector.h" +#include "components/scheduler/child/nestable_single_thread_task_runner.h" +#include "components/scheduler/child/prioritizing_task_queue_selector.h" #include "ui/gfx/frame_time.h" -namespace content { +namespace scheduler { RendererSchedulerImpl::RendererSchedulerImpl( scoped_refptr<NestableSingleThreadTaskRunner> main_task_runner) @@ -45,8 +45,7 @@ end_renderer_hidden_idle_period_closure_.Reset(base::Bind( &RendererSchedulerImpl::EndIdlePeriod, weak_factory_.GetWeakPtr())); - for (size_t i = SchedulerHelper::TASK_QUEUE_COUNT; - i < TASK_QUEUE_COUNT; + for (size_t i = SchedulerHelper::TASK_QUEUE_COUNT; i < TASK_QUEUE_COUNT; i++) { helper_.SetQueueName(i, TaskQueueIdToString(static_cast<QueueId>(i))); } @@ -133,10 +132,8 @@ // TODO(rmcilroy): Consider reducing the idle period based on the runtime of // the next pending delayed tasks (as currently done in for long idle times) helper_.StartIdlePeriod( - SchedulerHelper::IdlePeriodState::IN_SHORT_IDLE_PERIOD, - now, - estimated_next_frame_begin_, - true); + SchedulerHelper::IdlePeriodState::IN_SHORT_IDLE_PERIOD, now, + estimated_next_frame_begin_, true); } } @@ -168,8 +165,7 @@ base::TimeDelta end_idle_when_hidden_delay = base::TimeDelta::FromMilliseconds(kEndIdleWhenHiddenDelayMillis); control_task_runner_->PostDelayedTask( - FROM_HERE, - end_renderer_hidden_idle_period_closure_.callback(), + FROM_HERE, end_renderer_hidden_idle_period_closure_.callback(), end_idle_when_hidden_delay); renderer_hidden_ = true; @@ -303,8 +299,8 @@ } } -base::TimeTicks -RendererSchedulerImpl::CurrentIdleTaskDeadlineForTesting() const { +base::TimeTicks RendererSchedulerImpl::CurrentIdleTaskDeadlineForTesting() + const { base::TimeTicks deadline; helper_.CurrentIdleTaskDeadlineCallback(&deadline); return deadline; @@ -489,10 +485,6 @@ return &helper_; } -void RendererSchedulerImpl::SetWorkBatchSizeForTesting(size_t work_batch_size) { - helper_.SetWorkBatchSizeForTesting(work_batch_size); -} - RendererSchedulerImpl::PollableNeedsUpdateFlag::PollableNeedsUpdateFlag( base::Lock* write_lock_) : flag_(false), write_lock_(write_lock_) { @@ -515,8 +507,8 @@ helper_.CheckOnValidThread(); timer_queue_suspend_count_++; ForceUpdatePolicy(); - DCHECK(!helper_.SchedulerTaskQueueSelector()->IsQueueEnabled( - TIMER_TASK_QUEUE)); + DCHECK( + !helper_.SchedulerTaskQueueSelector()->IsQueueEnabled(TIMER_TASK_QUEUE)); } void RendererSchedulerImpl::ResumeTimerQueue() { @@ -638,4 +630,4 @@ return InputStreamState::ACTIVE; } -} // namespace content +} // namespace scheduler
diff --git a/content/renderer/scheduler/renderer_scheduler_impl.h b/components/scheduler/renderer/renderer_scheduler_impl.h similarity index 93% rename from content/renderer/scheduler/renderer_scheduler_impl.h rename to components/scheduler/renderer/renderer_scheduler_impl.h index 04c9ab8..6dcd3c9d 100644 --- a/content/renderer/scheduler/renderer_scheduler_impl.h +++ b/components/scheduler/renderer/renderer_scheduler_impl.h
@@ -2,14 +2,15 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CONTENT_RENDERER_SCHEDULER_RENDERER_SCHEDULER_IMPL_H_ -#define CONTENT_RENDERER_SCHEDULER_RENDERER_SCHEDULER_IMPL_H_ +#ifndef COMPONENTS_SCHEDULER_RENDERER_RENDERER_SCHEDULER_IMPL_H_ +#define COMPONENTS_SCHEDULER_RENDERER_RENDERER_SCHEDULER_IMPL_H_ #include "base/atomicops.h" #include "base/synchronization/lock.h" -#include "content/child/scheduler/scheduler_helper.h" -#include "content/renderer/scheduler/deadline_task_runner.h" -#include "content/renderer/scheduler/renderer_scheduler.h" +#include "components/scheduler/child/scheduler_helper.h" +#include "components/scheduler/renderer/deadline_task_runner.h" +#include "components/scheduler/renderer/renderer_scheduler.h" +#include "components/scheduler/scheduler_export.h" namespace base { namespace trace_event { @@ -17,9 +18,9 @@ } } -namespace content { +namespace scheduler { -class CONTENT_EXPORT RendererSchedulerImpl +class SCHEDULER_EXPORT RendererSchedulerImpl : public RendererScheduler, public SchedulerHelper::SchedulerHelperDelegate { public: @@ -52,7 +53,6 @@ void ResumeTimerQueue() override; SchedulerHelper* GetSchedulerHelperForTesting(); - void SetWorkBatchSizeForTesting(size_t work_batch_size); base::TimeTicks CurrentIdleTaskDeadlineForTesting() const; private: @@ -209,6 +209,6 @@ DISALLOW_COPY_AND_ASSIGN(RendererSchedulerImpl); }; -} // namespace content +} // namespace scheduler -#endif // CONTENT_RENDERER_SCHEDULER_RENDERER_SCHEDULER_IMPL_H_ +#endif // COMPONENTS_SCHEDULER_RENDERER_RENDERER_SCHEDULER_IMPL_H_
diff --git a/content/renderer/scheduler/renderer_scheduler_impl_unittest.cc b/components/scheduler/renderer/renderer_scheduler_impl_unittest.cc similarity index 96% rename from content/renderer/scheduler/renderer_scheduler_impl_unittest.cc rename to components/scheduler/renderer/renderer_scheduler_impl_unittest.cc index 150be2e..27ec431 100644 --- a/content/renderer/scheduler/renderer_scheduler_impl_unittest.cc +++ b/components/scheduler/renderer/renderer_scheduler_impl_unittest.cc
@@ -2,19 +2,19 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "content/renderer/scheduler/renderer_scheduler_impl.h" +#include "components/scheduler/renderer/renderer_scheduler_impl.h" #include "base/callback.h" #include "cc/output/begin_frame_args.h" #include "cc/test/ordered_simple_task_runner.h" #include "cc/test/test_now_source.h" -#include "content/child/scheduler/nestable_task_runner_for_test.h" -#include "content/child/scheduler/scheduler_message_loop_delegate.h" -#include "content/test/test_time_source.h" +#include "components/scheduler/child/nestable_task_runner_for_test.h" +#include "components/scheduler/child/scheduler_message_loop_delegate.h" +#include "components/scheduler/child/test_time_source.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" -namespace content { +namespace scheduler { namespace { class FakeInputEvent : public blink::WebInputEvent { @@ -45,17 +45,16 @@ void NullTask() { } -void AppendToVectorReentrantTask( - base::SingleThreadTaskRunner* task_runner, - std::vector<int>* vector, - int* reentrant_count, - int max_reentrant_count) { +void AppendToVectorReentrantTask(base::SingleThreadTaskRunner* task_runner, + std::vector<int>* vector, + int* reentrant_count, + int max_reentrant_count) { vector->push_back((*reentrant_count)++); if (*reentrant_count < max_reentrant_count) { task_runner->PostTask( - FROM_HERE, base::Bind(AppendToVectorReentrantTask, - base::Unretained(task_runner), vector, - reentrant_count, max_reentrant_count)); + FROM_HERE, + base::Bind(AppendToVectorReentrantTask, base::Unretained(task_runner), + vector, reentrant_count, max_reentrant_count)); } } @@ -68,15 +67,13 @@ int max_idle_task_reposts = 2; -void RepostingIdleTestTask( - SingleThreadIdleTaskRunner* idle_task_runner, - int* run_count, - base::TimeTicks deadline) { +void RepostingIdleTestTask(SingleThreadIdleTaskRunner* idle_task_runner, + int* run_count, + base::TimeTicks deadline) { if ((*run_count + 1) < max_idle_task_reposts) { idle_task_runner->PostIdleTask( - FROM_HERE, - base::Bind(&RepostingIdleTestTask, base::Unretained(idle_task_runner), - run_count)); + FROM_HERE, base::Bind(&RepostingIdleTestTask, + base::Unretained(idle_task_runner), run_count)); } (*run_count)++; } @@ -95,12 +92,11 @@ (*run_count)++; } -void PostingYieldingTestTask( - RendererSchedulerImpl* scheduler, - base::SingleThreadTaskRunner* task_runner, - bool simulate_input, - bool* should_yield_before, - bool* should_yield_after) { +void PostingYieldingTestTask(RendererSchedulerImpl* scheduler, + base::SingleThreadTaskRunner* task_runner, + bool simulate_input, + bool* should_yield_before, + bool* should_yield_after) { *should_yield_before = scheduler->ShouldYieldForHighPriorityWork(); task_runner->PostTask(FROM_HERE, base::Bind(NullTask)); if (simulate_input) { @@ -553,8 +549,7 @@ FakeInputEvent(blink::WebInputEvent::GestureScrollBegin)); RunUntilIdle(); - EXPECT_THAT(run_order, - testing::ElementsAre(std::string("L1"))); + EXPECT_THAT(run_order, testing::ElementsAre(std::string("L1"))); } TEST_F(RendererSchedulerImplTest, @@ -1167,8 +1162,8 @@ idle_task_runner_->PostIdleTask( FROM_HERE, base::Bind(&IdleTestTask, &run_count, &deadline_in_task)); - default_task_runner_->PostDelayedTask( - FROM_HERE, base::Bind(&NullTask), pending_task_delay); + default_task_runner_->PostDelayedTask(FROM_HERE, base::Bind(&NullTask), + pending_task_delay); scheduler_->BeginFrameNotExpectedSoon(); RunUntilIdle(); @@ -1182,8 +1177,8 @@ base::TimeTicks deadline_in_task; int run_count = 0; - default_task_runner_->PostDelayedTask( - FROM_HERE, base::Bind(&NullTask), pending_task_delay); + default_task_runner_->PostDelayedTask(FROM_HERE, base::Bind(&NullTask), + pending_task_delay); // Advance clock until after delayed task was meant to be run. clock_->AdvanceNow(base::TimeDelta::FromMilliseconds(20)); @@ -1253,8 +1248,7 @@ // Posting a after-wakeup idle task also shouldn't wake the scheduler or // initiate the next long idle period. idle_task_runner_->PostIdleTaskAfterWakeup( - FROM_HERE, - base::Bind(&IdleTestTask, &run_count, &deadline_in_task)); + FROM_HERE, base::Bind(&IdleTestTask, &run_count, &deadline_in_task)); RunUntilIdle(); new_idle_period_deadline = scheduler_->CurrentIdleTaskDeadlineForTesting(); EXPECT_EQ(idle_period_deadline, new_idle_period_deadline); @@ -1275,8 +1269,7 @@ int run_count = 0; idle_task_runner_->PostIdleTask( - FROM_HERE, - base::Bind(&IdleTestTask, &run_count, &deadline_in_task)); + FROM_HERE, base::Bind(&IdleTestTask, &run_count, &deadline_in_task)); // Observation of touchstart should defer the start of the long idle period. scheduler_->DidReceiveInputEventOnCompositorThread( @@ -1424,4 +1417,4 @@ testing::ElementsAre(std::string("T1"), std::string("T2"))); } -} // namespace content +} // namespace scheduler
diff --git a/content/renderer/scheduler/webthread_impl_for_renderer_scheduler.cc b/components/scheduler/renderer/webthread_impl_for_renderer_scheduler.cc similarity index 87% rename from content/renderer/scheduler/webthread_impl_for_renderer_scheduler.cc rename to components/scheduler/renderer/webthread_impl_for_renderer_scheduler.cc index 79ee88f..9bad0b3 100644 --- a/content/renderer/scheduler/webthread_impl_for_renderer_scheduler.cc +++ b/components/scheduler/renderer/webthread_impl_for_renderer_scheduler.cc
@@ -2,13 +2,13 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "content/renderer/scheduler/webthread_impl_for_renderer_scheduler.h" +#include "components/scheduler/renderer/webthread_impl_for_renderer_scheduler.h" -#include "content/child/scheduler/web_scheduler_impl.h" -#include "content/renderer/scheduler/renderer_scheduler.h" +#include "components/scheduler/child/web_scheduler_impl.h" +#include "components/scheduler/renderer/renderer_scheduler.h" #include "third_party/WebKit/public/platform/WebTraceLocation.h" -namespace content { +namespace scheduler { WebThreadImplForRendererScheduler::WebThreadImplForRendererScheduler( RendererScheduler* scheduler) @@ -58,4 +58,4 @@ scheduler_->RemoveTaskObserver(observer); } -} // namespace content +} // namespace scheduler
diff --git a/content/renderer/scheduler/webthread_impl_for_renderer_scheduler.h b/components/scheduler/renderer/webthread_impl_for_renderer_scheduler.h similarity index 75% rename from content/renderer/scheduler/webthread_impl_for_renderer_scheduler.h rename to components/scheduler/renderer/webthread_impl_for_renderer_scheduler.h index 6d1af5a..70202feb 100644 --- a/content/renderer/scheduler/webthread_impl_for_renderer_scheduler.h +++ b/components/scheduler/renderer/webthread_impl_for_renderer_scheduler.h
@@ -2,22 +2,22 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CONTENT_RENDERER_SCHEDULER_WEBTHREAD_IMPL_FOR_RENDERER_SCHEDULER_H_ -#define CONTENT_RENDERER_SCHEDULER_WEBTHREAD_IMPL_FOR_RENDERER_SCHEDULER_H_ +#ifndef COMPONENTS_SCHEDULER_RENDERER_WEBTHREAD_IMPL_FOR_RENDERER_SCHEDULER_H_ +#define COMPONENTS_SCHEDULER_RENDERER_WEBTHREAD_IMPL_FOR_RENDERER_SCHEDULER_H_ #include "base/containers/scoped_ptr_hash_map.h" -#include "content/child/webthread_base.h" +#include "components/scheduler/child/webthread_base.h" namespace blink { class WebScheduler; }; -namespace content { - +namespace scheduler { class RendererScheduler; class WebSchedulerImpl; -class CONTENT_EXPORT WebThreadImplForRendererScheduler : public WebThreadBase { +class SCHEDULER_EXPORT WebThreadImplForRendererScheduler + : public WebThreadBase { public: explicit WebThreadImplForRendererScheduler(RendererScheduler* scheduler); virtual ~WebThreadImplForRendererScheduler(); @@ -44,6 +44,6 @@ blink::PlatformThreadId thread_id_; }; -} // namespace content +} // namespace scheduler -#endif // CONTENT_RENDERER_SCHEDULER_WEBTHREAD_IMPL_FOR_RENDERER_SCHEDULER_H_ +#endif // COMPONENTS_SCHEDULER_RENDERER_WEBTHREAD_IMPL_FOR_RENDERER_SCHEDULER_H_
diff --git a/content/renderer/scheduler/webthread_impl_for_renderer_scheduler_unittest.cc b/components/scheduler/renderer/webthread_impl_for_renderer_scheduler_unittest.cc similarity index 91% rename from content/renderer/scheduler/webthread_impl_for_renderer_scheduler_unittest.cc rename to components/scheduler/renderer/webthread_impl_for_renderer_scheduler_unittest.cc index 191a2710..e92e7d3 100644 --- a/content/renderer/scheduler/webthread_impl_for_renderer_scheduler_unittest.cc +++ b/components/scheduler/renderer/webthread_impl_for_renderer_scheduler_unittest.cc
@@ -2,16 +2,16 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "content/renderer/scheduler/webthread_impl_for_renderer_scheduler.h" +#include "components/scheduler/renderer/webthread_impl_for_renderer_scheduler.h" #include "base/run_loop.h" -#include "content/child/scheduler/scheduler_message_loop_delegate.h" -#include "content/renderer/scheduler/renderer_scheduler_impl.h" +#include "components/scheduler/child/scheduler_message_loop_delegate.h" +#include "components/scheduler/renderer/renderer_scheduler_impl.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/WebKit/public/platform/WebTraceLocation.h" -namespace content { +namespace scheduler { namespace { const int kWorkBatchSize = 2; @@ -38,7 +38,8 @@ ~WebThreadImplForRendererSchedulerTest() override {} void SetWorkBatchSizeForTesting(size_t work_batch_size) { - scheduler_.SetWorkBatchSizeForTesting(work_batch_size); + scheduler_.GetSchedulerHelperForTesting()->SetWorkBatchSizeForTesting( + work_batch_size); } protected: @@ -80,7 +81,7 @@ thread_.addTaskObserver(&observer); scoped_ptr<MockTask> task(new MockTask()); - scheduler_.SetWorkBatchSizeForTesting(kWorkBatchSize); + SetWorkBatchSizeForTesting(kWorkBatchSize); { testing::InSequence sequence; EXPECT_CALL(observer, willProcessTask()); @@ -101,7 +102,7 @@ scoped_ptr<MockTask> task1(new MockTask()); scoped_ptr<MockTask> task2(new MockTask()); - scheduler_.SetWorkBatchSizeForTesting(kWorkBatchSize); + SetWorkBatchSizeForTesting(kWorkBatchSize); { testing::InSequence sequence; EXPECT_CALL(observer, willProcessTask()); @@ -128,7 +129,7 @@ scoped_ptr<MockTask> task2(new MockTask()); scoped_ptr<MockTask> task3(new MockTask()); - scheduler_.SetWorkBatchSizeForTesting(kWorkBatchSize); + SetWorkBatchSizeForTesting(kWorkBatchSize); { testing::InSequence sequence; EXPECT_CALL(observer, willProcessTask()); @@ -199,4 +200,4 @@ thread_.removeTaskObserver(&observer); } -} // namespace content +} // namespace scheduler
diff --git a/components/scheduler/scheduler.gni b/components/scheduler/scheduler.gni new file mode 100644 index 0000000..4a93aed --- /dev/null +++ b/components/scheduler/scheduler.gni
@@ -0,0 +1,12 @@ +# 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 the scheduler gypi values. This file is read once and +# cached, which is a performance optimization that allows us to share the +# results of parsing the .gypi file between the public and private BUILD.gn +# files. It also saves us from duplicating this exec_script call. +scheduler_gypi_values = exec_script("//build/gypi_to_gn.py", + [ rebase_path("scheduler.gypi") ], + "scope", + [ "scheduler.gypi" ])
diff --git a/components/scheduler/scheduler.gyp b/components/scheduler/scheduler.gyp new file mode 100644 index 0000000..a9c4402 --- /dev/null +++ b/components/scheduler/scheduler.gyp
@@ -0,0 +1,51 @@ +# 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. + +{ + '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, + }, + 'includes': [ + 'scheduler.gypi', + ], + 'targets': [ + { + # GN version: //components/scheduler:common + 'target_name': 'scheduler_common', + 'type': 'static_library', + 'include_dirs': [ + '../..', + ], + 'sources': [ + '<@(scheduler_common_sources)', + ], + }, + { + # GN version: //components/scheduler:scheduler + 'target_name': 'scheduler', + 'type': '<(component)', + 'dependencies': [ + 'scheduler_common', + '../../base/base.gyp:base', + '../../cc/cc.gyp:cc', + '../../third_party/WebKit/public/blink.gyp:blink', + '../../ui/gfx/gfx.gyp:gfx', + ], + 'include_dirs': [ + '../..', + ], + 'defines': [ + 'SCHEDULER_IMPLEMENTATION', + ], + # Disable c4267 warnings until we fix size_t to int truncations. + 'msvs_disabled_warnings': [ 4267, ], + 'sources': [ + '<@(scheduler_sources)', + ], + }, + ], +}
diff --git a/components/scheduler/scheduler.gypi b/components/scheduler/scheduler.gypi new file mode 100644 index 0000000..72a3f5c4b --- /dev/null +++ b/components/scheduler/scheduler.gypi
@@ -0,0 +1,56 @@ +# 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. + +{ + 'variables': { + 'scheduler_common_sources': [ + 'common/scheduler_switches.cc', + 'common/scheduler_switches.h', + ], + 'scheduler_sources': [ + 'child/cancelable_closure_holder.cc', + 'child/cancelable_closure_holder.h', + 'child/child_scheduler.h', + 'child/nestable_single_thread_task_runner.h', + 'child/null_idle_task_runner.cc', + 'child/null_idle_task_runner.h', + 'child/null_worker_scheduler.cc', + 'child/null_worker_scheduler.h', + 'child/prioritizing_task_queue_selector.cc', + 'child/prioritizing_task_queue_selector.h', + 'child/scheduler_helper.cc', + 'child/scheduler_helper.h', + 'child/scheduler_message_loop_delegate.cc', + 'child/scheduler_message_loop_delegate.h', + 'child/single_thread_idle_task_runner.cc', + 'child/single_thread_idle_task_runner.h', + 'child/task_queue_manager.cc', + 'child/task_queue_manager.h', + 'child/task_queue_selector.h', + 'child/time_source.cc', + 'child/time_source.h', + 'child/web_scheduler_impl.cc', + 'child/web_scheduler_impl.h', + 'child/webthread_base.cc', + 'child/webthread_base.h', + 'child/webthread_impl_for_worker_scheduler.cc', + 'child/webthread_impl_for_worker_scheduler.h', + 'child/worker_scheduler.cc', + 'child/worker_scheduler.h', + 'child/worker_scheduler_impl.cc', + 'child/worker_scheduler_impl.h', + 'renderer/deadline_task_runner.cc', + 'renderer/deadline_task_runner.h', + 'renderer/null_renderer_scheduler.cc', + 'renderer/null_renderer_scheduler.h', + 'renderer/renderer_scheduler.cc', + 'renderer/renderer_scheduler.h', + 'renderer/renderer_scheduler_impl.cc', + 'renderer/renderer_scheduler_impl.h', + 'renderer/webthread_impl_for_renderer_scheduler.cc', + 'renderer/webthread_impl_for_renderer_scheduler.h', + 'scheduler_export.h', + ], + }, +}
diff --git a/components/scheduler/scheduler_export.h b/components/scheduler/scheduler_export.h new file mode 100644 index 0000000..b441b84 --- /dev/null +++ b/components/scheduler/scheduler_export.h
@@ -0,0 +1,29 @@ +// 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 COMPONENTS_SCHEDULER_SCHEDULER_EXPORT_H_ +#define COMPONENTS_SCHEDULER_SCHEDULER_EXPORT_H_ + +#if defined(COMPONENT_BUILD) +#if defined(WIN32) + +#if defined(SCHEDULER_IMPLEMENTATION) +#define SCHEDULER_EXPORT __declspec(dllexport) +#else +#define SCHEDULER_EXPORT __declspec(dllimport) +#endif // defined(SCHEDULER_IMPLEMENTATION) + +#else // defined(WIN32) +#if defined(SCHEDULER_IMPLEMENTATION) +#define SCHEDULER_EXPORT __attribute__((visibility("default"))) +#else +#define SCHEDULER_EXPORT +#endif +#endif + +#else // defined(COMPONENT_BUILD) +#define SCHEDULER_EXPORT +#endif + +#endif // COMPONENTS_SCHEDULER_SCHEDULER_EXPORT_H_
diff --git a/components/search_engines/template_url.cc b/components/search_engines/template_url.cc index cb4c6db..8c565f2 100644 --- a/components/search_engines/template_url.cc +++ b/components/search_engines/template_url.cc
@@ -1430,7 +1430,7 @@ } std::string new_params(old_params, 0, search_terms_position.begin); - new_params += base::UTF16ToUTF8(search_terms_args.search_terms); + new_params += base::UTF16ToUTF8(encoded_terms); new_params += old_params.substr(search_terms_position.end()); GURL::Replacements replacements;
diff --git a/components/search_engines/template_url_unittest.cc b/components/search_engines/template_url_unittest.cc index 861ce79..2ab7428 100644 --- a/components/search_engines/template_url_unittest.cc +++ b/components/search_engines/template_url_unittest.cc
@@ -1154,8 +1154,8 @@ // Russian text encoded with UTF-8. EXPECT_TRUE(url.ExtractSearchTermsFromURL( - GURL("http://utf-8.ru/?q=\xD0\x97\xD0\xB4\xD1\x80\xD0\xB0\xD0\xB2\xD1\x81" - "\xD1\x82\xD0\xB2\xD1\x83\xD0\xB9,+\xD0\xBC\xD0\xB8\xD1\x80!"), + GURL("http://utf-8.ru/?q=%D0%97%D0%B4%D1%80%D0%B0%D0%B2%D1%81%D1%82" + "%D0%B2%D1%83%D0%B9,+%D0%BC%D0%B8%D1%80!"), search_terms_data_, &result)); EXPECT_EQ( base::WideToUTF16( @@ -1164,16 +1164,16 @@ result); EXPECT_TRUE(url.ExtractSearchTermsFromURL( - GURL("http://utf-8.ru/#q=\xD0\xB4\xD0\xB2\xD0\xB0+\xD1\x81\xD0\xBB" - "\xD0\xBE\xD0\xB2\xD0\xB0"), + GURL("http://utf-8.ru/#q=%D0%B4%D0%B2%D0%B0+%D1%81%D0%BB%D0%BE%D0%B2" + "%D0%B0"), search_terms_data_, &result)); EXPECT_EQ( base::WideToUTF16(L"\x0434\x0432\x0430 \x0441\x043B\x043E\x0432\x0430"), result); EXPECT_TRUE(url.ExtractSearchTermsFromURL( - GURL("http://utf-8.ru/path/\xD0\xB1\xD1\x83\xD0\xBA\xD0\xB2\xD1\x8B%20" - "\xD0\x90%20\xD0\xB8%20A"), + GURL("http://utf-8.ru/path/%D0%B1%D1%83%D0%BA%D0%B2%D1%8B%20%D0%90%20" + "%D0%B8%20A"), search_terms_data_, &result)); EXPECT_EQ( base::WideToUTF16(L"\x0431\x0443\x043A\x0432\x044B \x0410 \x0438 A"), @@ -1313,7 +1313,7 @@ EXPECT_TRUE(url.ReplaceSearchTermsInURL( GURL("http://google.com/?q=something"), search_terms, search_terms_data_, &result)); - EXPECT_EQ(GURL("http://google.com/?q=Bob%20Morane"), result); + EXPECT_EQ(GURL("http://google.com/?q=Bob+Morane"), result); result = GURL("http://should.not.change.com"); EXPECT_FALSE(url.ReplaceSearchTermsInURL( @@ -1328,7 +1328,7 @@ EXPECT_TRUE(url.ReplaceSearchTermsInURL( GURL("https://google.com/?q=foo"), search_terms, search_terms_data_, &result)); - EXPECT_EQ(GURL("https://google.com/?q=Bob%20Morane"), result); + EXPECT_EQ(GURL("https://google.com/?q=Bob+Morane"), result); EXPECT_FALSE(url.ReplaceSearchTermsInURL( GURL("http://google.com:8080/?q=foo"), search_terms, @@ -1337,26 +1337,26 @@ EXPECT_TRUE(url.ReplaceSearchTermsInURL( GURL("http://google.com/?q=1+2+3&b=456"), search_terms, search_terms_data_, &result)); - EXPECT_EQ(GURL("http://google.com/?q=Bob%20Morane&b=456"), result); + EXPECT_EQ(GURL("http://google.com/?q=Bob+Morane&b=456"), result); // Note: Spaces in REF parameters are not escaped. See TryEncoding() in // template_url.cc for details. EXPECT_TRUE(url.ReplaceSearchTermsInURL( GURL("http://google.com/alt/?q=123#q=456"), search_terms, search_terms_data_, &result)); - EXPECT_EQ(GURL("http://google.com/alt/?q=123#q=Bob Morane"), result); + EXPECT_EQ(GURL("http://google.com/alt/?q=123#q=Bob+Morane"), result); EXPECT_TRUE(url.ReplaceSearchTermsInURL( GURL("http://google.com/alt/?a=012&q=123&b=456#f=789"), search_terms, search_terms_data_, &result)); - EXPECT_EQ(GURL("http://google.com/alt/?a=012&q=Bob%20Morane&b=456#f=789"), + EXPECT_EQ(GURL("http://google.com/alt/?a=012&q=Bob+Morane&b=456#f=789"), result); EXPECT_TRUE(url.ReplaceSearchTermsInURL( GURL("http://google.com/alt/?a=012&q=123&b=456#j=abc&q=789&h=def9"), search_terms, search_terms_data_, &result)); EXPECT_EQ(GURL("http://google.com/alt/?a=012&q=123&b=456" - "#j=abc&q=Bob Morane&h=def9"), result); + "#j=abc&q=Bob+Morane&h=def9"), result); EXPECT_FALSE(url.ReplaceSearchTermsInURL( GURL("http://google.com/alt/?q="), search_terms, @@ -1377,7 +1377,7 @@ EXPECT_TRUE(url.ReplaceSearchTermsInURL( GURL("http://google.com/alt/?q=#q=123"), search_terms, search_terms_data_, &result)); - EXPECT_EQ(GURL("http://google.com/alt/?q=#q=Bob Morane"), result); + EXPECT_EQ(GURL("http://google.com/alt/?q=#q=Bob+Morane"), result); } TEST_F(TemplateURLTest, ReplaceSearchTermsInURLPath) { @@ -1404,6 +1404,75 @@ EXPECT_EQ(GURL("http://should.not.change.com"), result); } +// Checks that the ReplaceSearchTermsInURL function works correctly +// for search terms containing non-latin characters for a search engine +// using UTF-8 input encoding. +TEST_F(TemplateURLTest, ReplaceSearchTermsInUTF8URL) { + TemplateURLData data; + data.SetURL("http://utf-8.ru/?q={searchTerms}"); + data.alternate_urls.push_back("http://utf-8.ru/#q={searchTerms}"); + data.alternate_urls.push_back("http://utf-8.ru/path/{searchTerms}"); + TemplateURL url(data); + + // Russian text which will be encoded with UTF-8. + TemplateURLRef::SearchTermsArgs search_terms(base::WideToUTF16( + L"\x0442\x0435\x043A\x0441\x0442")); + GURL result; + + EXPECT_TRUE(url.ReplaceSearchTermsInURL( + GURL("http://utf-8.ru/?q=a+b"), search_terms, search_terms_data_, + &result)); + EXPECT_EQ(GURL("http://utf-8.ru/?q=%D1%82%D0%B5%D0%BA%D1%81%D1%82"), + result); + + EXPECT_TRUE(url.ReplaceSearchTermsInURL( + GURL("http://utf-8.ru/#q=a+b"), search_terms, search_terms_data_, + &result)); + EXPECT_EQ(GURL("http://utf-8.ru/#q=%D1%82%D0%B5%D0%BA%D1%81%D1%82"), + result); + + EXPECT_TRUE(url.ReplaceSearchTermsInURL( + GURL("http://utf-8.ru/path/a%20b"), search_terms, search_terms_data_, + &result)); + EXPECT_EQ(GURL("http://utf-8.ru/path/%D1%82%D0%B5%D0%BA%D1%81%D1%82"), + result); +} + +// Checks that the ReplaceSearchTermsInURL function works correctly +// for search terms containing non-latin characters for a search engine +// using non UTF-8 input encoding. +TEST_F(TemplateURLTest, ReplaceSearchTermsInNonUTF8URL) { + TemplateURLData data; + data.SetURL("http://windows-1251.ru/?q={searchTerms}"); + data.alternate_urls.push_back("http://windows-1251.ru/#q={searchTerms}"); + data.alternate_urls.push_back("http://windows-1251.ru/path/{searchTerms}"); + data.input_encodings.push_back("windows-1251"); + TemplateURL url(data); + + // Russian text which will be encoded with Windows-1251. + TemplateURLRef::SearchTermsArgs search_terms(base::WideToUTF16( + L"\x0442\x0435\x043A\x0441\x0442")); + GURL result; + + EXPECT_TRUE(url.ReplaceSearchTermsInURL( + GURL("http://windows-1251.ru/?q=a+b"), search_terms, search_terms_data_, + &result)); + EXPECT_EQ(GURL("http://windows-1251.ru/?q=%F2%E5%EA%F1%F2"), + result); + + EXPECT_TRUE(url.ReplaceSearchTermsInURL( + GURL("http://windows-1251.ru/#q=a+b"), search_terms, search_terms_data_, + &result)); + EXPECT_EQ(GURL("http://windows-1251.ru/#q=%F2%E5%EA%F1%F2"), + result); + + EXPECT_TRUE(url.ReplaceSearchTermsInURL( + GURL("http://windows-1251.ru/path/a%20b"), search_terms, + search_terms_data_, &result)); + EXPECT_EQ(GURL("http://windows-1251.ru/path/%F2%E5%EA%F1%F2"), + result); +} + // Test the |suggest_query_params| field of SearchTermsArgs. TEST_F(TemplateURLTest, SuggestQueryParams) { TemplateURLData data;
diff --git a/components/signin.gypi b/components/signin.gypi index 4030c5a1..2bad402 100644 --- a/components/signin.gypi +++ b/components/signin.gypi
@@ -62,8 +62,6 @@ 'signin/core/browser/profile_oauth2_token_service.h', 'signin/core/browser/refresh_token_annotation_request.cc', 'signin/core/browser/refresh_token_annotation_request.h', - 'signin/core/browser/signin_account_id_helper.cc', - 'signin/core/browser/signin_account_id_helper.h', 'signin/core/browser/signin_client.h', 'signin/core/browser/signin_error_controller.cc', 'signin/core/browser/signin_error_controller.h',
diff --git a/components/signin/core/browser/BUILD.gn b/components/signin/core/browser/BUILD.gn index df217e0b..5e9d59aa 100644 --- a/components/signin/core/browser/BUILD.gn +++ b/components/signin/core/browser/BUILD.gn
@@ -20,8 +20,6 @@ "profile_oauth2_token_service.h", "refresh_token_annotation_request.cc", "refresh_token_annotation_request.h", - "signin_account_id_helper.cc", - "signin_account_id_helper.h", "signin_client.h", "signin_error_controller.cc", "signin_error_controller.h",
diff --git a/components/signin/core/browser/signin_account_id_helper.cc b/components/signin/core/browser/signin_account_id_helper.cc deleted file mode 100644 index 9288fff3..0000000 --- a/components/signin/core/browser/signin_account_id_helper.cc +++ /dev/null
@@ -1,170 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "components/signin/core/browser/signin_account_id_helper.h" - -#include "base/prefs/pref_service.h" -#include "base/profiler/scoped_tracker.h" -#include "components/signin/core/browser/profile_oauth2_token_service.h" -#include "components/signin/core/browser/signin_client.h" -#include "components/signin/core/common/signin_pref_names.h" -#include "google_apis/gaia/gaia_oauth_client.h" - -// TODO(guohui): this class should be moved to a more generic place for reuse. -class SigninAccountIdHelper::GaiaIdFetcher - : public OAuth2TokenService::Consumer, - public gaia::GaiaOAuthClient::Delegate { - public: - GaiaIdFetcher(SigninClient* client, - ProfileOAuth2TokenService* token_service, - SigninManagerBase* signin_manager, - SigninAccountIdHelper* signin_account_id_helper); - ~GaiaIdFetcher() override; - - // OAuth2TokenService::Consumer implementation. - void OnGetTokenSuccess(const OAuth2TokenService::Request* request, - const std::string& access_token, - const base::Time& expiration_time) override; - void OnGetTokenFailure(const OAuth2TokenService::Request* request, - const GoogleServiceAuthError& error) override; - - // gaia::GaiaOAuthClient::Delegate implementation. - void OnGetUserIdResponse(const std::string& gaia_id) override; - void OnOAuthError() override; - void OnNetworkError(int response_code) override; - - private: - void Start(); - - SigninClient* client_; - ProfileOAuth2TokenService* token_service_; - SigninManagerBase* signin_manager_; - SigninAccountIdHelper* signin_account_id_helper_; - - scoped_ptr<OAuth2TokenService::Request> login_token_request_; - scoped_ptr<gaia::GaiaOAuthClient> gaia_oauth_client_; - - DISALLOW_COPY_AND_ASSIGN(GaiaIdFetcher); -}; - -SigninAccountIdHelper::GaiaIdFetcher::GaiaIdFetcher( - SigninClient* client, - ProfileOAuth2TokenService* token_service, - SigninManagerBase* signin_manager, - SigninAccountIdHelper* signin_account_id_helper) - : OAuth2TokenService::Consumer("gaia_id_fetcher"), - client_(client), - token_service_(token_service), - signin_manager_(signin_manager), - signin_account_id_helper_(signin_account_id_helper) { - Start(); -} - -SigninAccountIdHelper::GaiaIdFetcher::~GaiaIdFetcher() {} - -void SigninAccountIdHelper::GaiaIdFetcher::Start() { - OAuth2TokenService::ScopeSet scopes; - scopes.insert("https://www.googleapis.com/auth/userinfo.profile"); - login_token_request_ = token_service_->StartRequest( - signin_manager_->GetAuthenticatedAccountId(), scopes, this); -} - -void SigninAccountIdHelper::GaiaIdFetcher::OnGetTokenSuccess( - const OAuth2TokenService::Request* request, - const std::string& access_token, - const base::Time& expiration_time) { - DCHECK_EQ(request, login_token_request_.get()); - - gaia_oauth_client_.reset( - new gaia::GaiaOAuthClient(client_->GetURLRequestContext())); - - const int kMaxGetUserIdRetries = 3; - gaia_oauth_client_->GetUserId(access_token, kMaxGetUserIdRetries, this); -} - -void SigninAccountIdHelper::GaiaIdFetcher::OnGetTokenFailure( - const OAuth2TokenService::Request* request, - const GoogleServiceAuthError& error) { - VLOG(1) << "OnGetTokenFailure: " << error.error_message(); - DCHECK_EQ(request, login_token_request_.get()); - signin_account_id_helper_->OnPrimaryAccountIdFetched(""); -} - -void SigninAccountIdHelper::GaiaIdFetcher::OnGetUserIdResponse( - const std::string& gaia_id) { - signin_account_id_helper_->OnPrimaryAccountIdFetched(gaia_id); -} - -void SigninAccountIdHelper::GaiaIdFetcher::OnOAuthError() { - VLOG(1) << "OnOAuthError"; -} - -void SigninAccountIdHelper::GaiaIdFetcher::OnNetworkError(int response_code) { - VLOG(1) << "OnNetworkError " << response_code; -} - -SigninAccountIdHelper::SigninAccountIdHelper( - SigninClient* client, - ProfileOAuth2TokenService* token_service, - SigninManagerBase* signin_manager) - : client_(client), - token_service_(token_service), - signin_manager_(signin_manager) { - DCHECK(client_); - DCHECK(token_service_); - DCHECK(signin_manager_); - signin_manager_->AddObserver(this); - std::string primary_email = signin_manager_->GetAuthenticatedAccountId(); - if (!primary_email.empty() && - token_service_->RefreshTokenIsAvailable(primary_email) && - !disable_for_test_) { - id_fetcher_.reset( - new GaiaIdFetcher(client_, token_service_, signin_manager_, this)); - } - token_service_->AddObserver(this); -} - -SigninAccountIdHelper::~SigninAccountIdHelper() { - signin_manager_->RemoveObserver(this); - token_service_->RemoveObserver(this); -} - -void SigninAccountIdHelper::GoogleSignedOut(const std::string& account_id, - const std::string& username) { - client_->GetPrefs()->ClearPref(prefs::kGoogleServicesUserAccountId); -} - -void SigninAccountIdHelper::OnRefreshTokenAvailable( - const std::string& account_id) { - // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460 is - // fixed. - tracked_objects::ScopedTracker tracking_profile( - FROM_HERE_WITH_EXPLICIT_FUNCTION( - "422460 SigninAccountIdHelper::OnRefreshTokenAvailable")); - - if (account_id == signin_manager_->GetAuthenticatedAccountId()) { - std::string current_gaia_id = - client_->GetPrefs()->GetString(prefs::kGoogleServicesUserAccountId); - if (current_gaia_id.empty() && !disable_for_test_) { - id_fetcher_.reset( - new GaiaIdFetcher(client_, token_service_, signin_manager_, this)); - } - } -} - -void SigninAccountIdHelper::OnPrimaryAccountIdFetched( - const std::string& gaia_id) { - if (!gaia_id.empty()) { - client_->GetPrefs()->SetString(prefs::kGoogleServicesUserAccountId, - gaia_id); - } -} - -// static -bool SigninAccountIdHelper::disable_for_test_ = false; - -// static -void SigninAccountIdHelper::SetDisableForTest(bool disable_for_test) { - disable_for_test_ = disable_for_test; -}
diff --git a/components/signin/core/browser/signin_account_id_helper.h b/components/signin/core/browser/signin_account_id_helper.h deleted file mode 100644 index 8c93896..0000000 --- a/components/signin/core/browser/signin_account_id_helper.h +++ /dev/null
@@ -1,57 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef COMPONENTS_SIGNIN_CORE_BROWSER_SIGNIN_ACCOUNT_ID_HELPER_H_ -#define COMPONENTS_SIGNIN_CORE_BROWSER_SIGNIN_ACCOUNT_ID_HELPER_H_ - -#include "components/signin/core/browser/signin_manager.h" -#include "google_apis/gaia/gaia_oauth_client.h" -#include "google_apis/gaia/oauth2_token_service.h" - -class CookieSettings; -class GaiaAuthFetcher; -class ProfileOAuth2TokenService; -class SigninClient; - -// The helper class for managing the obfuscated GAIA ID of the primary -// account. It fetches the ID when user first signs into Chrome or when user -// opens a connected Chrome profile without an obfuscated GAIA ID, and stores -// the ID in the profile preference. -class SigninAccountIdHelper : public SigninManagerBase::Observer, - public OAuth2TokenService::Observer { - public: - SigninAccountIdHelper(SigninClient* client, - ProfileOAuth2TokenService* token_service, - SigninManagerBase* signin_manager); - ~SigninAccountIdHelper() override; - - // SigninManagerBase::Observer: - void GoogleSignedOut(const std::string& account_id, - const std::string& username) override; - - // OAuth2TokenService::Observer: - void OnRefreshTokenAvailable(const std::string& account_id) override; - - // Disables network requests for testing to avoid messing up with irrelevant - // tests. - static void SetDisableForTest(bool disable_for_test); - - private: - // Invoked when receiving the response for |account_id_fetcher_|. - void OnPrimaryAccountIdFetched(const std::string& gaia_id); - - // Helper class for fetching the obfuscated account ID. - class GaiaIdFetcher; - scoped_ptr<GaiaIdFetcher> id_fetcher_; - - static bool disable_for_test_; - - SigninClient* client_; - ProfileOAuth2TokenService* token_service_; - SigninManagerBase* signin_manager_; - - DISALLOW_COPY_AND_ASSIGN(SigninAccountIdHelper); -}; - -#endif // COMPONENTS_SIGNIN_CORE_BROWSER_SIGNIN_ACCOUNT_ID_HELPER_H_
diff --git a/components/storage_monitor/image_capture_device.mm b/components/storage_monitor/image_capture_device.mm index 1008bfd..29285d9 100644 --- a/components/storage_monitor/image_capture_device.mm +++ b/components/storage_monitor/image_capture_device.mm
@@ -13,7 +13,7 @@ base::File::Error RenameFile(const base::FilePath& downloaded_filename, const base::FilePath& desired_filename) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE)); + DCHECK_CURRENTLY_ON(content::BrowserThread::FILE); bool success = base::ReplaceFile(downloaded_filename, desired_filename, NULL); return success ? base::File::FILE_OK : base::File::FILE_ERROR_NOT_FOUND; } @@ -22,7 +22,7 @@ base::WeakPtr<ImageCaptureDeviceListener> listener, const std::string& name, const base::File::Error& result) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); if (listener) listener->DownloadedFile(name, result); } @@ -75,18 +75,18 @@ - (void)setListener:(base::WeakPtr<storage_monitor::ImageCaptureDeviceListener>) listener { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); listener_ = listener; } - (void)open { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); DCHECK(listener_); [camera_ requestOpenSession]; } - (void)close { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); closing_ = true; [camera_ cancelDownload]; [camera_ requestCloseSession]; @@ -100,7 +100,7 @@ - (void)downloadFile:(const std::string&)name localPath:(const base::FilePath&)localPath { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); // Find the file with that name and start download. for (ICCameraItem* item in [camera_ mediaFiles]) {
diff --git a/components/storage_monitor/mtab_watcher_linux.cc b/components/storage_monitor/mtab_watcher_linux.cc index 00a9d0cc..31dda986 100644 --- a/components/storage_monitor/mtab_watcher_linux.cc +++ b/components/storage_monitor/mtab_watcher_linux.cc
@@ -38,7 +38,7 @@ : mtab_path_(mtab_path), delegate_(delegate), weak_ptr_factory_(this) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE)); + DCHECK_CURRENTLY_ON(content::BrowserThread::FILE); bool ret = file_watcher_.Watch( mtab_path_, false, base::Bind(&MtabWatcherLinux::OnFilePathChanged, @@ -52,11 +52,11 @@ } MtabWatcherLinux::~MtabWatcherLinux() { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE)); + DCHECK_CURRENTLY_ON(content::BrowserThread::FILE); } void MtabWatcherLinux::ReadMtab() const { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE)); + DCHECK_CURRENTLY_ON(content::BrowserThread::FILE); FILE* fp = setmntent(mtab_path_.value().c_str(), "r"); if (!fp) @@ -87,7 +87,7 @@ void MtabWatcherLinux::OnFilePathChanged( const base::FilePath& path, bool error) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE)); + DCHECK_CURRENTLY_ON(content::BrowserThread::FILE); if (path != mtab_path_) { // This cannot happen unless FilePathWatcher is buggy. Just ignore this
diff --git a/components/storage_monitor/portable_device_watcher_win.cc b/components/storage_monitor/portable_device_watcher_win.cc index 3f90fee..8095a89d 100644 --- a/components/storage_monitor/portable_device_watcher_win.cc +++ b/components/storage_monitor/portable_device_watcher_win.cc
@@ -489,7 +489,7 @@ } void PortableDeviceWatcherWin::Init(HWND hwnd) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); notifications_ = RegisterPortableDeviceNotification(hwnd); base::SequencedWorkerPool* pool = content::BrowserThread::GetBlockingPool(); media_task_runner_ = pool->GetSequencedTaskRunnerWithShutdownBehavior( @@ -499,7 +499,7 @@ } void PortableDeviceWatcherWin::OnWindowMessage(UINT event_type, LPARAM data) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); if (!IsPortableDeviceStructure(data)) return; @@ -514,7 +514,7 @@ const std::string& storage_device_id, base::string16* device_location, base::string16* storage_object_id) const { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); DCHECK(device_location); DCHECK(storage_object_id); MTPStorageMap::const_iterator storage_map_iter = @@ -573,7 +573,7 @@ void PortableDeviceWatcherWin::EnumerateAttachedDevices() { DCHECK(media_task_runner_.get()); - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); Devices* devices = new Devices; base::PostTaskAndReplyWithResult( media_task_runner_.get(), FROM_HERE, @@ -584,7 +584,7 @@ void PortableDeviceWatcherWin::OnDidEnumerateAttachedDevices( const Devices* devices, const bool result) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); DCHECK(devices); if (!result) return; @@ -597,7 +597,7 @@ void PortableDeviceWatcherWin::HandleDeviceAttachEvent( const base::string16& pnp_device_id) { DCHECK(media_task_runner_.get()); - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); DeviceDetails* device_details = new DeviceDetails; base::PostTaskAndReplyWithResult( media_task_runner_.get(), FROM_HERE, @@ -609,7 +609,7 @@ void PortableDeviceWatcherWin::OnDidHandleDeviceAttachEvent( const DeviceDetails* device_details, const bool result) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); DCHECK(device_details); if (!result) return; @@ -646,7 +646,7 @@ void PortableDeviceWatcherWin::HandleDeviceDetachEvent( const base::string16& pnp_device_id) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); MTPDeviceMap::iterator device_iter = device_map_.find(pnp_device_id); if (device_iter == device_map_.end()) return;
diff --git a/components/storage_monitor/storage_monitor_chromeos.cc b/components/storage_monitor/storage_monitor_chromeos.cc index 92e7cfd..6227ee0 100644 --- a/components/storage_monitor/storage_monitor_chromeos.cc +++ b/components/storage_monitor/storage_monitor_chromeos.cc
@@ -79,7 +79,7 @@ // Returns whether the mount point in |mount_info| is a media device or not. bool CheckMountedPathOnFileThread( const DiskMountManager::MountPointInfo& mount_info) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE)); + DCHECK_CURRENTLY_ON(content::BrowserThread::FILE); return MediaStorageUtil::HasDcim(base::FilePath(mount_info.mount_path)); } @@ -115,7 +115,7 @@ } void StorageMonitorCros::CheckExistingMountPoints() { - DCHECK_CURRENTLY_ON(BrowserThread::UI); + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); const DiskMountManager::MountPointMap& mount_point_map = DiskMountManager::GetInstance()->mount_points(); for (DiskMountManager::MountPointMap::const_iterator it = @@ -148,7 +148,7 @@ DiskMountManager::MountEvent event, chromeos::MountError error_code, const DiskMountManager::MountPointInfo& mount_info) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); // Ignore mount points that are not devices. if (mount_info.mount_type != chromeos::MOUNT_TYPE_DEVICE) @@ -277,7 +277,7 @@ void StorageMonitorCros::AddMountedPath( const DiskMountManager::MountPointInfo& mount_info, bool has_dcim) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); if (ContainsKey(mount_map_, mount_info.mount_path)) { // CheckExistingMountPointsOnUIThread() added the mount point information
diff --git a/components/storage_monitor/storage_monitor_mac.mm b/components/storage_monitor/storage_monitor_mac.mm index d8055dc..22a884b 100644 --- a/components/storage_monitor/storage_monitor_mac.mm +++ b/components/storage_monitor/storage_monitor_mac.mm
@@ -47,7 +47,7 @@ StorageInfo BuildStorageInfo( CFDictionaryRef dict, std::string* bsd_name) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE)); + DCHECK_CURRENTLY_ON(content::BrowserThread::FILE); CFStringRef device_bsd_name = base::mac::GetValueFromDictionary<CFStringRef>( dict, kDADiskDescriptionMediaBSDNameKey); @@ -109,7 +109,7 @@ const base::WeakPtr<StorageMonitorMac>& monitor, base::ScopedCFTypeRef<CFDictionaryRef> dict, StorageMonitorMac::UpdateType update_type) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE)); + DCHECK_CURRENTLY_ON(content::BrowserThread::FILE); std::string bsd_name; StorageInfo info = BuildStorageInfo(dict, &bsd_name); @@ -209,7 +209,7 @@ const std::string& bsd_name, const StorageInfo& info, UpdateType update_type) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); pending_disk_updates_--; bool initialization_complete = false; @@ -348,7 +348,7 @@ void StorageMonitorMac::GetDiskInfoAndUpdate( DADiskRef disk, StorageMonitorMac::UpdateType update_type) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); pending_disk_updates_++;
diff --git a/components/test/DEPS b/components/test/DEPS index ad35ec8..ec6ab2f 100644 --- a/components/test/DEPS +++ b/components/test/DEPS
@@ -2,7 +2,17 @@ # To initialize the global data of content_settings. "+components/content_settings/core/common", "+components/invalidation/android/component_jni_registrar.h", + "+content/public/android/java/src/org/chromium/content/browser", + "+content/public/app/content_jni_onload.h", + "+content/public/app/content_main.h", + "+content/public/common/content_switches.h", "+content/public/test", + "+content/shell/android/java/src/org/chromium/content_shell", + "+content/shell/android/shell_jni_registrar.h", + "+content/shell/app/shell_main_delegate.h", + "+jni", + "+media/base/media_switches.h", + "+ui/android/java/src/org/chromium/ui/base", "+ui/base/android/ui_base_jni_registrar.h", "+ui/base/resource/resource_bundle.h", "+ui/base/ui_base_paths.h",
diff --git a/components/test/android/OWNERS b/components/test/android/OWNERS new file mode 100644 index 0000000..c43b4b3 --- /dev/null +++ b/components/test/android/OWNERS
@@ -0,0 +1,3 @@ +jaekyun@chromium.org +sievers@chromium.org +tedchoc@chromium.org
diff --git a/components/test/android/browsertests_apk/AndroidManifest.xml.jinja2 b/components/test/android/browsertests_apk/AndroidManifest.xml.jinja2 new file mode 100644 index 0000000..3ac010d --- /dev/null +++ b/components/test/android/browsertests_apk/AndroidManifest.xml.jinja2
@@ -0,0 +1,60 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- 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. + --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="org.chromium.components_browsertests_apk"> + + <application android:name="ComponentsBrowserTestsApplication" + android:label="ComponentsBrowserTests"> + <activity android:name="ComponentsBrowserTestsActivity" + 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.MAIN"/> + <category android:name="android.intent.category.LAUNCHER"/> + </intent-filter> + </activity> + <!-- The following service entries exist in order to allow us to + start more than one sandboxed process. --> + + <!-- NOTE: If you change the values of "android:process" for any of the below services, + you also need to update kHelperProcessExecutableName in chrome_constants.cc. --> + {% set num_sandboxed_services = 20 %} + <meta-data android:name="org.chromium.content.browser.NUM_SANDBOXED_SERVICES" + android:value="{{ num_sandboxed_services }}"/> + {% for i in range(num_sandboxed_services) %} + <service android:name="org.chromium.content.app.SandboxedProcessService{{ i }}" + android:process=":sandboxed_process{{ i }}" + android:isolatedProcess="true" + android:exported="false" /> + {% endfor %} + + {% set num_privileged_services = 3 %} + <meta-data android:name="org.chromium.content.browser.NUM_PRIVILEGED_SERVICES" + android:value="{{ num_privileged_services }}"/> + {% for i in range(num_privileged_services) %} + <service android:name="org.chromium.content.app.PrivilegedProcessService{{ i }}" + android:process=":privileged_process{{ i }}" + android:isolatedProcess="false" + android:exported="false" /> + {% endfor %} + </application> + + <uses-sdk android:minSdkVersion="16" android:targetSdkVersion="22" /> + <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/> + <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> + <uses-permission android:name="android.permission.CAMERA" /> + <uses-permission android:name="android.permission.INTERNET"/> + <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/> + <uses-permission android:name="android.permission.RECORD_AUDIO"/> + <uses-permission android:name="android.permission.VIBRATE"/> + <uses-permission android:name="android.permission.WAKE_LOCK"/> + <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> +</manifest>
diff --git a/components/test/android/browsertests_apk/components_browser_tests_android.cc b/components/test/android/browsertests_apk/components_browser_tests_android.cc new file mode 100644 index 0000000..baf8efe --- /dev/null +++ b/components/test/android/browsertests_apk/components_browser_tests_android.cc
@@ -0,0 +1,99 @@ +// 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 class sets up the environment for running the content browser tests +// inside an android application. + +#include <android/log.h> +#include <unistd.h> + +#include "base/android/base_jni_registrar.h" +#include "base/android/fifo_utils.h" +#include "base/android/jni_android.h" +#include "base/android/jni_string.h" +#include "base/android/library_loader/library_loader_hooks.h" +#include "base/android/scoped_java_ref.h" +#include "base/base_switches.h" +#include "base/command_line.h" +#include "base/files/file_path.h" +#include "base/logging.h" +#include "base/strings/string_tokenizer.h" +#include "base/strings/string_util.h" +#include "base/strings/stringprintf.h" +#include "content/public/common/content_switches.h" +#include "content/public/test/test_launcher.h" +#include "jni/ComponentsBrowserTestsActivity_jni.h" +#include "media/base/media_switches.h" +#include "testing/android/native_test/native_test_util.h" + +using testing::native_test_util::ArgsToArgv; +using testing::native_test_util::ParseArgsFromCommandLineFile; +using testing::native_test_util::ScopedMainEntryLogger; + +// The main function of the program to be wrapped as an apk. +extern int main(int argc, char** argv); + +namespace { + +// The test runner script writes the command line file in +// "/data/local/tmp". +static const char kCommandLineFilePath[] = + "/data/local/tmp/components-browser-tests-command-line"; + +} // namespace + +namespace components { + +// TODO(jaekyun): Refactor and deduplicate with +// testing/android/native_test/native_test_launcher.cc (http://crbug.com/476410) +static void RunTests(JNIEnv* env, + jobject obj, + jstring jfiles_dir, + jobject app_context) { + // Command line basic initialization, will be fully initialized later. + static const char* const kInitialArgv[] = {"ComponentsBrowserTestsActivity"}; + base::CommandLine::Init(arraysize(kInitialArgv), kInitialArgv); + + // Set the application context in base. + base::android::ScopedJavaLocalRef<jobject> scoped_context( + env, env->NewLocalRef(app_context)); + base::android::InitApplicationContext(env, scoped_context); + base::android::RegisterJni(env); + + std::vector<std::string> args; + ParseArgsFromCommandLineFile(kCommandLineFilePath, &args); + + std::vector<char*> argv; + int argc = ArgsToArgv(args, &argv); + + // Fully initialize command line with arguments. + base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); + command_line->AppendArguments(base::CommandLine(argc, &argv[0]), false); + + // Append required switches. + command_line->AppendSwitch(content::kSingleProcessTestsFlag); + command_line->AppendSwitch(switches::kUseFakeDeviceForMediaStream); + command_line->AppendSwitch(switches::kUseFakeUIForMediaStream); + // Specify a socket name to not conflict with the default one used + // in content_shell. + command_line->AppendSwitchASCII(switches::kRemoteDebuggingSocketName, + "components_browsertests_devtools_remote"); + + // Create fifo and redirect stdout and stderr to it. + base::FilePath files_dir( + base::android::ConvertJavaStringToUTF8(env, jfiles_dir)); + base::FilePath fifo_path(files_dir.Append(base::FilePath("test.fifo"))); + base::android::CreateFIFO(fifo_path, 0666); + base::android::RedirectStream(stdout, fifo_path, "w+"); + dup2(STDOUT_FILENO, STDERR_FILENO); + + ScopedMainEntryLogger scoped_main_entry_logger; + main(argc, &argv[0]); +} + +bool RegisterComponentsBrowserTestsAndroid(JNIEnv* env) { + return RegisterNativesImpl(env); +} + +} // namespace components
diff --git a/components/test/android/browsertests_apk/components_browser_tests_android.h b/components/test/android/browsertests_apk/components_browser_tests_android.h new file mode 100644 index 0000000..a989706 --- /dev/null +++ b/components/test/android/browsertests_apk/components_browser_tests_android.h
@@ -0,0 +1,16 @@ +// 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 COMPONENTS_TEST_ANDROID_BROWSERTESTS_APK_COMPONENTS_BROWSER_TESTS_ANDROID_H_ +#define COMPONENTS_TEST_ANDROID_BROWSERTESTS_APK_COMPONENTS_BROWSER_TESTS_ANDROID_H_ + +#include <jni.h> + +namespace components { + +bool RegisterComponentsBrowserTestsAndroid(JNIEnv* env); + +} // namespace components + +#endif // COMPONENTS_TEST_ANDROID_BROWSERTESTS_APK_COMPONENTS_BROWSER_TESTS_ANDROID_H_
diff --git a/components/test/android/browsertests_apk/components_browser_tests_jni_onload.cc b/components/test/android/browsertests_apk/components_browser_tests_jni_onload.cc new file mode 100644 index 0000000..e47b840 --- /dev/null +++ b/components/test/android/browsertests_apk/components_browser_tests_jni_onload.cc
@@ -0,0 +1,40 @@ +// 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/android/jni_android.h" +#include "base/bind.h" +#include "components/test/android/browsertests_apk/components_browser_tests_android.h" +#include "content/public/app/content_jni_onload.h" +#include "content/public/app/content_main.h" +#include "content/public/test/nested_message_pump_android.h" +#include "content/shell/android/shell_jni_registrar.h" +#include "content/shell/app/shell_main_delegate.h" + +namespace { + +bool RegisterJNI(JNIEnv* env) { + return content::android::RegisterShellJni(env) && + content::NestedMessagePumpAndroid::RegisterJni(env) && + components::RegisterComponentsBrowserTestsAndroid(env); +} + +bool Init() { + content::SetContentMainDelegate(new content::ShellMainDelegate()); + return true; +} + +} // 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)); + std::vector<base::android::InitCallback> init_callbacks; + init_callbacks.push_back(base::Bind(&Init)); + if (!content::android::OnJNIOnLoadRegisterJNI(vm, register_callbacks) || + !content::android::OnJNIOnLoadInit(init_callbacks)) { + return -1; + } + return JNI_VERSION_1_4; +}
diff --git a/components/test/android/browsertests_apk/res/layout/test_activity.xml b/components/test/android/browsertests_apk/res/layout/test_activity.xml new file mode 100644 index 0000000..227ce1f --- /dev/null +++ b/components/test/android/browsertests_apk/res/layout/test_activity.xml
@@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- 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. + --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" android:layout_height="match_parent" + android:orientation="vertical"> + <org.chromium.content_shell.ShellManager + android:id="@+id/shell_container" + android:layout_width="match_parent" + android:layout_height="match_parent" /> +</LinearLayout>
diff --git a/components/test/android/browsertests_apk/src/org/chromium/components_browsertests_apk/ComponentsBrowserTestsActivity.java b/components/test/android/browsertests_apk/src/org/chromium/components_browsertests_apk/ComponentsBrowserTestsActivity.java new file mode 100644 index 0000000..bbf2461 --- /dev/null +++ b/components/test/android/browsertests_apk/src/org/chromium/components_browsertests_apk/ComponentsBrowserTestsActivity.java
@@ -0,0 +1,75 @@ +// 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.components_browsertests_apk; + +import android.app.Activity; +import android.content.Context; +import android.os.Bundle; +import android.os.Handler; +import android.util.Log; +import android.view.Window; +import android.view.WindowManager; + +import org.chromium.base.JNINamespace; +import org.chromium.base.annotations.SuppressFBWarnings; +import org.chromium.base.library_loader.LibraryLoader; +import org.chromium.base.library_loader.LibraryProcessType; +import org.chromium.base.library_loader.ProcessInitException; +import org.chromium.content.browser.BrowserStartupController; +import org.chromium.content_shell.ShellManager; +import org.chromium.ui.base.ActivityWindowAndroid; +import org.chromium.ui.base.WindowAndroid; + +/** + * Android activity for running components browser tests + */ +@JNINamespace("components") +public class ComponentsBrowserTestsActivity extends Activity { + private static final String TAG = "ComponentsBrowserTestsActivity"; + + private ShellManager mShellManager; + private WindowAndroid mWindowAndroid; + + @Override + @SuppressFBWarnings("DM_EXIT") + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + try { + LibraryLoader.get(LibraryProcessType.PROCESS_BROWSER).ensureInitialized(); + } catch (ProcessInitException e) { + Log.i(TAG, "Cannot load components_browsertests:" + e); + System.exit(-1); + } + BrowserStartupController.get(getApplicationContext(), LibraryProcessType.PROCESS_BROWSER) + .initChromiumBrowserProcessForTests(); + + setContentView(R.layout.test_activity); + mShellManager = (ShellManager) findViewById(R.id.shell_container); + mWindowAndroid = new ActivityWindowAndroid(this); + mShellManager.setWindow(mWindowAndroid, false); + + Window wind = this.getWindow(); + wind.addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD); + wind.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED); + wind.addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON); + + new Handler().post(new Runnable() { + @Override + public void run() { + Log.i(TAG, "Running tests"); + runTests(); + Log.i(TAG, "Tests finished."); + finish(); + } + }); + } + + private void runTests() { + nativeRunTests(getFilesDir().getAbsolutePath(), getApplicationContext()); + } + + private native void nativeRunTests(String filesDir, Context appContext); +}
diff --git a/components/test/android/browsertests_apk/src/org/chromium/components_browsertests_apk/ComponentsBrowserTestsApplication.java b/components/test/android/browsertests_apk/src/org/chromium/components_browsertests_apk/ComponentsBrowserTestsApplication.java new file mode 100644 index 0000000..91ee597b --- /dev/null +++ b/components/test/android/browsertests_apk/src/org/chromium/components_browsertests_apk/ComponentsBrowserTestsApplication.java
@@ -0,0 +1,32 @@ +// 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.components_browsertests_apk; + +import android.content.Context; + +import org.chromium.base.BaseChromiumApplication; +import org.chromium.base.PathUtils; +import org.chromium.base.ResourceExtractor; + +/** + * A basic content browser tests {@link android.app.Application}. + */ +public class ComponentsBrowserTestsApplication extends BaseChromiumApplication { + private static final String[] MANDATORY_PAK_FILES = + new String[] {"components_tests_resources.pak", "content_shell.pak", "icudtl.dat", + "natives_blob.bin", "snapshot_blob.bin"}; + private static final String PRIVATE_DATA_DIRECTORY_SUFFIX = "components_shell"; + + @Override + public void onCreate() { + super.onCreate(); + initializeApplicationParameters(this); + } + + public static void initializeApplicationParameters(Context context) { + ResourceExtractor.setMandatoryPaksToExtract(MANDATORY_PAK_FILES); + PathUtils.setPrivateDataDirectorySuffix(PRIVATE_DATA_DIRECTORY_SUFFIX, context); + } +}
diff --git a/components/test/data/password_manager/automated_tests/environment.py b/components/test/data/password_manager/automated_tests/environment.py index fd52754..37434650 100644 --- a/components/test/data/password_manager/automated_tests/environment.py +++ b/components/test/data/password_manager/automated_tests/environment.py
@@ -288,7 +288,12 @@ getattr(websitetest, test_case_name)() except Exception as e: successful = False - error = e.message + # httplib.CannotSendRequest doesn't define a message, + # so type(e).__name__ will at least log exception name as a reason. + # TODO(melandory): logging.exception(e) produces meaningful result + # for httplib.CannotSendRequest, so we can try to propagate information + # that reason is an exception to the logging phase. + error = "Exception %s %s" % (type(e).__name__, e) self.tests_results.append( (websitetest.name, test_case_name, successful, error))
diff --git a/components/test/data/password_manager/automated_tests/run_tests.py b/components/test/data/password_manager/automated_tests/run_tests.py index d1118f0..ef114da 100644 --- a/components/test/data/password_manager/automated_tests/run_tests.py +++ b/components/test/data/password_manager/automated_tests/run_tests.py
@@ -35,15 +35,20 @@ You have to set up appropriate logging handlers to have the logs appear. """ -import argparse import ConfigParser +import Queue +import argparse import logging +import multiprocessing import os import shutil -import subprocess +import stopit import tempfile import time +from threading import Thread +from collections import defaultdict + import tests @@ -51,126 +56,90 @@ # of logging.DEBUG, which is already used for detailed test debug messages. SCRIPT_DEBUG = 9 +class Config: + test_cases_to_run = tests.TEST_CASES + save_only_fails = False + tests_to_run = tests.all_tests.keys() + max_tests_in_parallel = 1 -class TestRunner(object): - """Runs tests for a single website.""" + def __init__(self, config_path): + config = ConfigParser.ConfigParser() + config.read(config_path) + if config.has_option("run_options", "tests_in_parallel"): + self.max_tests_in_parallel = config.getint( + "run_options", "tests_in_parallel") - def __init__(self, test_cmd, test_name): - """Initialize the TestRunner. + self.chrome_path = config.get("binaries", "chrome-path") + self.chromedriver_path = config.get("binaries", "chromedriver-path") + self.passwords_path = config.get("data_files", "passwords_path") - Args: - test_cmd: List of command line arguments to be supplied to - every test run. - test_name: Test name (e.g., facebook). - """ - self.logger = logging.getLogger("run_tests") + if config.has_option("run_options", "tests_to_run"): + self.tests_to_run = config.get("run_options", "tests_to_run").split(",") - self.profile_path = tempfile.mkdtemp() - results = tempfile.NamedTemporaryFile(delete=False) - self.results_path = results.name - results.close() - self.test_cmd = test_cmd + ["--profile-path", self.profile_path, - "--save-path", self.results_path] - self.test_name = test_name - # TODO(vabr): Ideally we would replace timeout with something allowing - # calling tests directly inside Python, and working on other platforms. - # - # The website test runs multiple scenarios, each one has an internal - # timeout of 200s for waiting (see |remaining_time_to_wait| and - # Wait() in websitetest.py). Expecting that not every scenario should - # take 200s, the maximum time allocated for all of them is 300s. - self.test_cmd = ["timeout", "300"] + self.test_cmd - - self.logger.log(SCRIPT_DEBUG, - "TestRunner set up for test %s, command '%s', " - "profile path %s, results file %s", - self.test_name, self.test_cmd, self.profile_path, - self.results_path) - - self.runner_process = None - # The tests can be flaky. This is why we try to rerun up to 3 times. - self.max_test_runs_left = 3 - self.failures = [] - self._run_test() - - def get_test_result(self): - """Return the test results. - - Returns: - (True, []) if the test passed. - (False, list_of_failures) if the test failed. - None if the test is still running. - """ - - test_running = self.runner_process and self.runner_process.poll() is None - if test_running: - return None - # Test is not running, now we have to check if we want to start it again. - if self._check_if_test_passed(): - self.logger.log(SCRIPT_DEBUG, "Test %s passed", self.test_name) - return True, [] - if self.max_test_runs_left == 0: - self.logger.log(SCRIPT_DEBUG, "Test %s failed", self.test_name) - return False, self.failures - self._run_test() - return None - - def _check_if_test_passed(self): - """Returns True if and only if the test passed.""" - - success = False - if os.path.isfile(self.results_path): - with open(self.results_path, "r") as results: - # TODO(vabr): Parse the results to make sure all scenarios succeeded - # instead of hard-coding here the number of tests scenarios from - # test.py:main. - NUMBER_OF_TEST_SCENARIOS = 3 - passed_scenarios = 0 - for line in results: - self.failures.append(line) - passed_scenarios += line.count("successful='True'") - success = passed_scenarios == NUMBER_OF_TEST_SCENARIOS - if success: - break - - self.logger.log( - SCRIPT_DEBUG, - "Test run of {0} has succeeded: {1}".format(self.test_name, success)) - return success - - def _run_test(self): - """Executes the command to run the test.""" - with open(self.results_path, "w"): - pass # Just clear the results file. - shutil.rmtree(path=self.profile_path, ignore_errors=True) - self.max_test_runs_left -= 1 - self.logger.log(SCRIPT_DEBUG, "Run of test %s started", self.test_name) - self.runner_process = subprocess.Popen(self.test_cmd) + if config.has_option("run_options", "test_cases_to_run"): + self.test_cases_to_run = config.get( + "run_options", "test_cases_to_run").split(",") + if (config.has_option("logging", "save-only-fails")): + self.save_only_fails = config.getboolean("logging", "save-only-fails") -def _apply_defaults(config, defaults): - """Adds default values from |defaults| to |config|. +def LogResultsOfTestRun(config, results): + """ Logs |results| of a test run. """ + logger = logging.getLogger("run_tests") + failed_tests = [] + failed_tests_num = 0 + for result in results: + website, test_case, success, reason = result + if not (config.save_only_fails and success): + logger.debug("Test case %s has %s on Website %s", test_case, + website, {True: "passed", False: "failed"}[success]) + if not success: + logger.debug("Reason of failure: %s", reason) - Note: This differs from ConfigParser's mechanism for providing defaults in - two aspects: - * The "defaults" here become explicit, and are associated with sections. - * Sections get created for the added defaults where needed, that is, if - they do not exist before. + if not success: + failed_tests.append("%s.%s" % (website, test_case)) + failed_tests_num += 1 - Args: - config: A ConfigParser instance to be updated - defaults: A dictionary mapping (section_string, option_string) pairs - to string values. For every section/option combination not already - contained in |config|, the value from |defaults| is stored in |config|. + logger.info("%d failed test cases out of %d, failing test cases: %s", + failed_tests_num, len(results), + sorted([name for name in failed_tests])) + + +def RunTestCaseOnWebsite((website, test_case, config)): + """ Runs a |test_case| on a |website|. In case when |test_case| has + failed it tries to rerun it. If run takes too long, then it is stopped. """ - for (section, option) in defaults: - if not config.has_section(section): - config.add_section(section) - if not config.has_option(section, option): - config.set(section, option, defaults[(section, option)]) + + profile_path = tempfile.mkdtemp() + # The tests can be flaky. This is why we try to rerun up to 3 times. + attempts = 3 + result = ("", "", False, "") + logger = logging.getLogger("run_tests") + for _ in xrange(attempts): + shutil.rmtree(path=profile_path, ignore_errors=True) + logger.log(SCRIPT_DEBUG, "Run of test case %s of website %s started", + test_case, website) + try: + with stopit.ThreadingTimeout(100) as timeout: + logger.log(SCRIPT_DEBUG, + "Run test with parameters: %s %s %s %s %s %s", + config.chrome_path, config.chromedriver_path, + profile_path, config.passwords_path, + website, test_case) + result = tests.RunTest(config.chrome_path, config.chromedriver_path, + profile_path, config.passwords_path, + website, test_case)[0] + if timeout != timeout.EXECUTED: + result = (website, test_case, False, "Timeout") + _, _, success, _ = result + if success: + return result + except Exception as e: + result = (website, test_case, False, e) + return result -def run_tests(config_path): +def RunTests(config_path): """Runs automated tests. Runs the tests and returns the results through logging: @@ -183,61 +152,21 @@ config_path: The path to the config INI file. See the top of the file for format description. """ - def has_test_run_finished(runner, result): - result = runner.get_test_result() - if result: # This test run is finished. - status, log = result - results.append((runner.test_name, status, log)) - return True - else: - return False - - defaults = {("run_options", "tests_in_parallel"): "1"} - config = ConfigParser.ConfigParser() - _apply_defaults(config, defaults) - config.read(config_path) - max_tests_in_parallel = config.getint("run_options", "tests_in_parallel") - full_path = os.path.realpath(__file__) - tests_dir = os.path.dirname(full_path) - tests_path = os.path.join(tests_dir, "tests.py") - test_name_idx = 2 # Index of "test_name_placeholder" below. - general_test_cmd = ["python", tests_path, "test_name_placeholder", - "--chrome-path", config.get("binaries", "chrome-path"), - "--chromedriver-path", - config.get("binaries", "chromedriver-path"), - "--passwords-path", - config.get("data_files", "passwords_path")] - runners = [] - if config.has_option("run_options", "tests_to_run"): - tests_to_run = config.get("run_options", "tests_to_run").split(",") - else: - tests_to_run = tests.all_tests.keys() - if (config.has_option("logging", "save-only-failures") and - config.getboolean("logging", "save-only-failures")): - general_test_cmd.append("--save-only-failures") - - if config.has_option("run_options", "test_cases_to_run"): - general_test_cmd += ["--test-cases-to-run", - config.get("run_options", "test_cases_to_run").replace(",", " ")] - + config = Config(config_path) logger = logging.getLogger("run_tests") - logger.log(SCRIPT_DEBUG, "%d tests to run: %s", len(tests_to_run), - tests_to_run) - results = [] # List of (name, bool_passed, failure_log). - while len(runners) + len(tests_to_run) > 0: - runners = [runner for runner in runners if not has_test_run_finished( - runner, results)] - while len(runners) < max_tests_in_parallel and len(tests_to_run): - test_name = tests_to_run.pop() - specific_test_cmd = list(general_test_cmd) - specific_test_cmd[test_name_idx] = test_name - runners.append(TestRunner(specific_test_cmd, test_name)) - time.sleep(1) - failed_tests = [(name, log) for (name, passed, log) in results if not passed] - logger.info("%d failed tests out of %d, failing tests: %s", - len(failed_tests), len(results), - [name for (name, _) in failed_tests]) - logger.debug("Logs of failing tests: %s", failed_tests) + logger.log(SCRIPT_DEBUG, "%d tests to run: %s", len(config.tests_to_run), + config.tests_to_run) + data = [(website, test_case, config) + for website in config.tests_to_run + for test_case in config.test_cases_to_run] + number_of_processes = min([config.max_tests_in_parallel, + len(config.test_cases_to_run) * + len(config.tests_to_run)]) + p = multiprocessing.Pool(number_of_processes) + results = p.map(RunTestCaseOnWebsite, data) + p.close() + p.join() + LogResultsOfTestRun(config, results) def main(): @@ -245,7 +174,7 @@ parser.add_argument("config_path", metavar="N", help="Path to the config.ini file.") args = parser.parse_args() - run_tests(args.config_path) + RunTests(args.config_path) if __name__ == "__main__":
diff --git a/components/test/data/password_manager/automated_tests/tests.py b/components/test/data/password_manager/automated_tests/tests.py index bddbafb1..f8fe6ff 100644 --- a/components/test/data/password_manager/automated_tests/tests.py +++ b/components/test/data/password_manager/automated_tests/tests.py
@@ -11,6 +11,9 @@ from websitetest import WebsiteTest +TEST_CASES = ("PromptFailTest", "PromptSuccessTest", "SaveAndAutofillTest") + + class Alexa(WebsiteTest): def Login(self): @@ -504,8 +507,10 @@ with open(environment_save_path, "w") as save_file: save_file.write(xml) + def RunTest(chrome_path, chromedriver_path, profile_path, - environment_passwords_path, website_test_name, test_case_name): + environment_passwords_path, website_test_name, + test_case_name): """Runs the test for the specified website. Args: @@ -528,15 +533,16 @@ environment = Environment(chrome_path, chromedriver_path, profile_path, environment_passwords_path, enable_automatic_password_saving) + try: + if website_test_name in all_tests: + environment.AddWebsiteTest(all_tests[website_test_name]) + else: + raise Exception("Test name {} is unknown.".format(website_test_name)) - if website_test_name in all_tests: - environment.AddWebsiteTest(all_tests[website_test_name]) - else: - raise Exception("Test name {} is unknown.".format(website_test_name)) - - environment.RunTestsOnSites(test_case_name) - environment.Quit() - return environment.tests_results + environment.RunTestsOnSites(test_case_name) + return environment.tests_results + finally: + environment.Quit() def main(): parser = argparse.ArgumentParser( @@ -575,9 +581,7 @@ if args.save_path: save_path = args.save_path - test_cases_to_run = args.test_cases_to_run or\ - ("PromptFailTest", "PromptSuccessTest", "SaveAndAutofillTest") - + test_cases_to_run = args.test_cases_to_run or TEST_CASES for test_case in test_cases_to_run: tests_results = RunTest( args.chrome_path, args.chromedriver_path, args.profile_path,
diff --git a/components/tracing/child_memory_dump_manager_delegate_impl.cc b/components/tracing/child_memory_dump_manager_delegate_impl.cc index 5595b14..f8048d9 100644 --- a/components/tracing/child_memory_dump_manager_delegate_impl.cc +++ b/components/tracing/child_memory_dump_manager_delegate_impl.cc
@@ -68,4 +68,8 @@ ctmf_->SendGlobalMemoryDumpRequest(args, callback); } +bool ChildMemoryDumpManagerDelegateImpl::IsCoordinatorProcess() const { + return false; +} + } // namespace tracing
diff --git a/components/tracing/child_memory_dump_manager_delegate_impl.h b/components/tracing/child_memory_dump_manager_delegate_impl.h index 1938ac5..912815dd 100644 --- a/components/tracing/child_memory_dump_manager_delegate_impl.h +++ b/components/tracing/child_memory_dump_manager_delegate_impl.h
@@ -33,6 +33,7 @@ void RequestGlobalMemoryDump( const base::trace_event::MemoryDumpRequestArgs& args, const base::trace_event::MemoryDumpCallback& callback) override; + bool IsCoordinatorProcess() const override; void SetChildTraceMessageFilter(ChildTraceMessageFilter* ctmf);
diff --git a/components/tracing_nacl.gyp b/components/tracing_nacl.gyp index d0fc45d..022a6c6 100644 --- a/components/tracing_nacl.gyp +++ b/components/tracing_nacl.gyp
@@ -19,7 +19,6 @@ '../base/base_nacl.gyp:base_nacl_nonsfi', '../ipc/ipc_nacl.gyp:ipc_nacl', '../ipc/ipc_nacl.gyp:ipc_nacl_nonsfi', - '../native_client/tools.gyp:prep_toolchain', ], 'include_dirs': [ '..',
diff --git a/components/undo/BUILD.gn b/components/undo/BUILD.gn index b7c4266..250e023 100644 --- a/components/undo/BUILD.gn +++ b/components/undo/BUILD.gn
@@ -36,4 +36,5 @@ "//components/bookmarks/test", "//testing/gtest", ] + configs += [ "//build/config/compiler:no_size_t_to_int_warning" ] }
diff --git a/components/user_manager/BUILD.gn b/components/user_manager/BUILD.gn index 7a5143e88..37f2893 100644 --- a/components/user_manager/BUILD.gn +++ b/components/user_manager/BUILD.gn
@@ -38,6 +38,7 @@ "user_type.h", ] deps += [ + "//base:prefs", "//chromeos:chromeos", "//components/session_manager/core", "//google_apis",
diff --git a/components/view_manager/BUILD.gn b/components/view_manager/BUILD.gn index 04b3988..9ae3c4d0 100644 --- a/components/view_manager/BUILD.gn +++ b/components/view_manager/BUILD.gn
@@ -86,7 +86,6 @@ "//third_party/mojo_services/src/geometry/public/interfaces", "//third_party/mojo_services/src/input_events/public/interfaces", "//third_party/mojo_services/src/native_viewport/public/interfaces", - "//third_party/mojo_services/src/surfaces/public/cpp", "//third_party/mojo_services/src/surfaces/public/interfaces", "//third_party/mojo_services/src/view_manager/public/interfaces", "//third_party/mojo_services/src/view_manager/public/cpp:common",
diff --git a/components/view_manager/display_manager.cc b/components/view_manager/display_manager.cc index 52825edca..05eafc1 100644 --- a/components/view_manager/display_manager.cc +++ b/components/view_manager/display_manager.cc
@@ -10,11 +10,11 @@ #include "components/view_manager/view_coordinate_conversions.h" #include "mojo/converters/geometry/geometry_type_converters.h" #include "mojo/converters/surfaces/surfaces_type_converters.h" +#include "mojo/converters/surfaces/surfaces_utils.h" #include "mojo/converters/transform/transform_type_converters.h" #include "third_party/mojo/src/mojo/public/cpp/application/application_connection.h" #include "third_party/mojo/src/mojo/public/cpp/application/application_impl.h" #include "third_party/mojo_services/src/gpu/public/interfaces/gpu.mojom.h" -#include "third_party/mojo_services/src/surfaces/public/cpp/surfaces_utils.h" #include "third_party/mojo_services/src/surfaces/public/interfaces/quads.mojom.h" #include "third_party/mojo_services/src/surfaces/public/interfaces/surfaces.mojom.h" @@ -61,7 +61,7 @@ base::saturated_cast<int32_t>(pass->shared_quad_states.size()); surface_quad->surface_quad_state = surface_quad_state.Pass(); - auto sqs = CreateDefaultSQS(*Size::From(view->bounds().size())); + auto sqs = mojo::CreateDefaultSQS(view->bounds().size()); sqs->blend_mode = mojo::SK_XFERMODE_kSrcOver_Mode; sqs->opacity = combined_opacity; sqs->content_to_target_transform = mojo::Transform::From(node_transform); @@ -135,10 +135,8 @@ } void DefaultDisplayManager::Draw() { - Rect rect; - rect.width = metrics_.size->width; - rect.height = metrics_.size->height; - auto pass = CreateDefaultPass(1, rect); + gfx::Rect rect(metrics_.size->width, metrics_.size->height); + auto pass = mojo::CreateDefaultPass(1, rect); pass->damage_rect = Rect::From(dirty_rect_); DrawViewTree(pass.get(), connection_manager_->root(), gfx::Vector2d(), 1.0f);
diff --git a/components/webcrypto/crypto_data.cc b/components/webcrypto/crypto_data.cc index f5b3c203..dd369ca 100644 --- a/components/webcrypto/crypto_data.cc +++ b/components/webcrypto/crypto_data.cc
@@ -15,17 +15,19 @@ } CryptoData::CryptoData(const std::vector<unsigned char>& bytes) - : bytes_(vector_as_array(&bytes)), byte_length_(bytes.size()) { + : bytes_(vector_as_array(&bytes)), + byte_length_(static_cast<unsigned int>(bytes.size())) { } CryptoData::CryptoData(const std::string& bytes) : bytes_(bytes.size() ? reinterpret_cast<const unsigned char*>(bytes.data()) : NULL), - byte_length_(bytes.size()) { + byte_length_(static_cast<unsigned int>(bytes.size())) { } CryptoData::CryptoData(const blink::WebVector<unsigned char>& bytes) - : bytes_(bytes.data()), byte_length_(bytes.size()) { + : bytes_(bytes.data()), + byte_length_(static_cast<unsigned int>(bytes.size())) { } } // namespace webcrypto
diff --git a/components/webcrypto/jwk.cc b/components/webcrypto/jwk.cc index e3e2de6..4e1781d 100644 --- a/components/webcrypto/jwk.cc +++ b/components/webcrypto/jwk.cc
@@ -416,7 +416,7 @@ } std::string MakeJwkAesAlgorithmName(const std::string& suffix, - unsigned int keylen_bytes) { + size_t keylen_bytes) { if (keylen_bytes == 16) return std::string("A128") + suffix; if (keylen_bytes == 24)
diff --git a/components/webcrypto/jwk.h b/components/webcrypto/jwk.h index 8c548ac..5ab62fee 100644 --- a/components/webcrypto/jwk.h +++ b/components/webcrypto/jwk.h
@@ -153,7 +153,7 @@ // Creates an AES algorithm name for the given key size (in bytes). For // instance "A128CBC" is the result of suffix="CBC", keylen_bytes=16. std::string MakeJwkAesAlgorithmName(const std::string& suffix, - unsigned int keylen_bytes); + size_t keylen_bytes); // This is very similar to ReadSecretKeyJwk(), except instead of specifying an // absolute "expected_alg", the suffix for an AES algorithm name is given
diff --git a/components/webcrypto/openssl/aes_cbc_openssl.cc b/components/webcrypto/openssl/aes_cbc_openssl.cc index bfbe57e..4d1ece2 100644 --- a/components/webcrypto/openssl/aes_cbc_openssl.cc +++ b/components/webcrypto/openssl/aes_cbc_openssl.cc
@@ -22,7 +22,7 @@ namespace { -const EVP_CIPHER* GetAESCipherByKeyLength(unsigned int key_length_bytes) { +const EVP_CIPHER* GetAESCipherByKeyLength(size_t key_length_bytes) { // BoringSSL does not support 192-bit AES keys. switch (key_length_bytes) { case 16:
diff --git a/components/webcrypto/openssl/aes_ctr_openssl.cc b/components/webcrypto/openssl/aes_ctr_openssl.cc index 1f5b299..26363f72 100644 --- a/components/webcrypto/openssl/aes_ctr_openssl.cc +++ b/components/webcrypto/openssl/aes_ctr_openssl.cc
@@ -23,7 +23,7 @@ namespace { -const EVP_CIPHER* GetAESCipherByKeyLength(unsigned int key_length_bytes) { +const EVP_CIPHER* GetAESCipherByKeyLength(size_t key_length_bytes) { // BoringSSL does not support 192-bit AES keys. switch (key_length_bytes) { case 16: @@ -119,7 +119,7 @@ counter_block.bytes(), counter_block.bytes() + counter_block.byte_length()); - unsigned int index = new_counter_block.size() - counter_length_bytes; + size_t index = new_counter_block.size() - counter_length_bytes; memset(&new_counter_block.front() + index, 0, counter_length_bytes); if (counter_length_bits_remainder) {
diff --git a/components/webcrypto/openssl/aes_gcm_openssl.cc b/components/webcrypto/openssl/aes_gcm_openssl.cc index d122276..564b54f8 100644 --- a/components/webcrypto/openssl/aes_gcm_openssl.cc +++ b/components/webcrypto/openssl/aes_gcm_openssl.cc
@@ -21,7 +21,7 @@ namespace { -const EVP_AEAD* GetAesGcmAlgorithmFromKeySize(unsigned int key_size_bytes) { +const EVP_AEAD* GetAesGcmAlgorithmFromKeySize(size_t key_size_bytes) { switch (key_size_bytes) { case 16: return EVP_aead_aes_128_gcm();
diff --git a/components/webcrypto/openssl/aes_kw_openssl.cc b/components/webcrypto/openssl/aes_kw_openssl.cc index 498d675..31dacea 100644 --- a/components/webcrypto/openssl/aes_kw_openssl.cc +++ b/components/webcrypto/openssl/aes_kw_openssl.cc
@@ -20,7 +20,7 @@ namespace { -const EVP_AEAD* GetAesKwAlgorithmFromKeySize(unsigned int key_size_bytes) { +const EVP_AEAD* GetAesKwAlgorithmFromKeySize(size_t key_size_bytes) { switch (key_size_bytes) { case 16: return EVP_aead_aes_128_key_wrap();
diff --git a/components/webcrypto/openssl/hmac_openssl.cc b/components/webcrypto/openssl/hmac_openssl.cc index 012c114..9962bfd 100644 --- a/components/webcrypto/openssl/hmac_openssl.cc +++ b/components/webcrypto/openssl/hmac_openssl.cc
@@ -34,7 +34,7 @@ const EVP_MD* digest_algorithm = GetDigest(hash.id()); if (!digest_algorithm) return Status::ErrorUnsupported(); - unsigned int hmac_expected_length = EVP_MD_size(digest_algorithm); + size_t hmac_expected_length = EVP_MD_size(digest_algorithm); buffer->resize(hmac_expected_length); crypto::ScopedOpenSSLSafeSizeBuffer<EVP_MAX_MD_SIZE> hmac_result(
diff --git a/components/webcrypto/openssl/rsa_hashed_algorithm_openssl.cc b/components/webcrypto/openssl/rsa_hashed_algorithm_openssl.cc index 337b9cf..9857a07 100644 --- a/components/webcrypto/openssl/rsa_hashed_algorithm_openssl.cc +++ b/components/webcrypto/openssl/rsa_hashed_algorithm_openssl.cc
@@ -47,7 +47,8 @@ return Status::ErrorUnexpected(); *key_algorithm = blink::WebCryptoKeyAlgorithm::createRsaHashed( - rsa_algorithm, modulus_length_bits, &e[0], e.size(), hash_algorithm); + rsa_algorithm, modulus_length_bits, &e[0], + static_cast<unsigned int>(e.size()), hash_algorithm); return Status::Success(); }
diff --git a/components/webcrypto/test/aes_cbc_unittest.cc b/components/webcrypto/test/aes_cbc_unittest.cc index e1eebe0..0f6ae85 100644 --- a/components/webcrypto/test/aes_cbc_unittest.cc +++ b/components/webcrypto/test/aes_cbc_unittest.cc
@@ -20,7 +20,8 @@ const std::vector<uint8_t>& iv) { return blink::WebCryptoAlgorithm::adoptParamsAndCreate( blink::WebCryptoAlgorithmIdAesCbc, - new blink::WebCryptoAesCbcParams(vector_as_array(&iv), iv.size())); + new blink::WebCryptoAesCbcParams(vector_as_array(&iv), + static_cast<unsigned int>(iv.size()))); } blink::WebCryptoAlgorithm CreateAesCbcKeyGenAlgorithm(
diff --git a/components/webcrypto/test/aes_ctr_unittest.cc b/components/webcrypto/test/aes_ctr_unittest.cc index b968672f..96453a1 100644 --- a/components/webcrypto/test/aes_ctr_unittest.cc +++ b/components/webcrypto/test/aes_ctr_unittest.cc
@@ -29,8 +29,9 @@ uint8_t length_bits) { return blink::WebCryptoAlgorithm::adoptParamsAndCreate( blink::WebCryptoAlgorithmIdAesCtr, - new blink::WebCryptoAesCtrParams(length_bits, vector_as_array(&counter), - counter.size())); + new blink::WebCryptoAesCtrParams( + length_bits, vector_as_array(&counter), + static_cast<unsigned int>(counter.size()))); } TEST(WebCryptoAesCtrTest, EncryptDecryptKnownAnswer) {
diff --git a/components/webcrypto/test/aes_gcm_unittest.cc b/components/webcrypto/test/aes_gcm_unittest.cc index c5ad1df..2d651492 100644 --- a/components/webcrypto/test/aes_gcm_unittest.cc +++ b/components/webcrypto/test/aes_gcm_unittest.cc
@@ -24,10 +24,11 @@ EXPECT_TRUE(SupportsAesGcm()); return blink::WebCryptoAlgorithm::adoptParamsAndCreate( blink::WebCryptoAlgorithmIdAesGcm, - new blink::WebCryptoAesGcmParams(vector_as_array(&iv), iv.size(), true, - vector_as_array(&additional_data), - additional_data.size(), true, - tag_length_bits)); + new blink::WebCryptoAesGcmParams( + vector_as_array(&iv), static_cast<unsigned int>(iv.size()), true, + vector_as_array(&additional_data), + static_cast<unsigned int>(additional_data.size()), true, + tag_length_bits)); } blink::WebCryptoAlgorithm CreateAesGcmKeyGenAlgorithm( @@ -173,7 +174,8 @@ GetBytesFromHexString(test, "plain_text"); const std::vector<uint8_t> test_authentication_tag = GetBytesFromHexString(test, "authentication_tag"); - const unsigned int test_tag_size_bits = test_authentication_tag.size() * 8; + const unsigned int test_tag_size_bits = + static_cast<unsigned int>(test_authentication_tag.size()) * 8; const std::vector<uint8_t> test_cipher_text = GetBytesFromHexString(test, "cipher_text");
diff --git a/components/webcrypto/test/hmac_unittest.cc b/components/webcrypto/test/hmac_unittest.cc index 6c84f85..f00c764 100644 --- a/components/webcrypto/test/hmac_unittest.cc +++ b/components/webcrypto/test/hmac_unittest.cc
@@ -90,7 +90,8 @@ // Ensure truncated signature does not verify by passing one less byte. EXPECT_EQ(Status::Success(), Verify(algorithm, key, - CryptoData(vector_as_array(&output), output.size() - 1), + CryptoData(vector_as_array(&output), + static_cast<unsigned int>(output.size()) - 1), CryptoData(test_message), &signature_match)); EXPECT_FALSE(signature_match);
diff --git a/components/webcrypto/test/rsa_oaep_unittest.cc b/components/webcrypto/test/rsa_oaep_unittest.cc index 9fb882b8..34352967 100644 --- a/components/webcrypto/test/rsa_oaep_unittest.cc +++ b/components/webcrypto/test/rsa_oaep_unittest.cc
@@ -23,8 +23,9 @@ const std::vector<uint8_t>& label) { return blink::WebCryptoAlgorithm::adoptParamsAndCreate( blink::WebCryptoAlgorithmIdRsaOaep, - new blink::WebCryptoRsaOaepParams(!label.empty(), vector_as_array(&label), - label.size())); + new blink::WebCryptoRsaOaepParams( + !label.empty(), vector_as_array(&label), + static_cast<unsigned int>(label.size()))); } scoped_ptr<base::DictionaryValue> CreatePublicKeyJwkDict() {
diff --git a/components/webcrypto/test/rsa_ssa_unittest.cc b/components/webcrypto/test/rsa_ssa_unittest.cc index 55a1a1c28..3170a96 100644 --- a/components/webcrypto/test/rsa_ssa_unittest.cc +++ b/components/webcrypto/test/rsa_ssa_unittest.cc
@@ -559,10 +559,11 @@ Sign(algorithm, private_key, CryptoData(data), &signature)); // Ensure truncated signature does not verify by passing one less byte. - EXPECT_EQ(Status::Success(), Verify(algorithm, public_key, - CryptoData(vector_as_array(&signature), - signature.size() - 1), - CryptoData(data), &signature_match)); + EXPECT_EQ(Status::Success(), + Verify(algorithm, public_key, + CryptoData(vector_as_array(&signature), + static_cast<unsigned int>(signature.size()) - 1), + CryptoData(data), &signature_match)); EXPECT_FALSE(signature_match); // Ensure truncated signature does not verify by passing no bytes.
diff --git a/components/webcrypto/test/sha_unittest.cc b/components/webcrypto/test/sha_unittest.cc index e6c553c2..a22c90d 100644 --- a/components/webcrypto/test/sha_unittest.cc +++ b/components/webcrypto/test/sha_unittest.cc
@@ -67,7 +67,8 @@ size_t chunk_length = std::min(kChunkSizeBytes, length - chunk_index); std::vector<uint8_t> chunk(begin, begin + chunk_length); ASSERT_TRUE(chunk.size() > 0); - EXPECT_TRUE(digestor->consume(vector_as_array(&chunk), chunk.size())); + EXPECT_TRUE(digestor->consume(vector_as_array(&chunk), + static_cast<unsigned int>(chunk.size()))); chunk_index = chunk_index + chunk_length; begin = begin + chunk_length; }
diff --git a/components/webcrypto/test/test_helpers.cc b/components/webcrypto/test/test_helpers.cc index 7d3ff449..76da7fa 100644 --- a/components/webcrypto/test/test_helpers.cc +++ b/components/webcrypto/test/test_helpers.cc
@@ -124,7 +124,7 @@ bool SupportsRsaPrivateKeyImport() { // TODO(eroman): Exclude version test for OS_CHROMEOS -#if defined(USE_NSS_CERTS) +#if !defined(USE_OPENSSL) && defined(USE_NSS_CERTS) crypto::EnsureNSSInit(); if (!NSS_VersionCheck("3.16.2")) { LOG(WARNING) << "RSA key import is not supported by this version of NSS. " @@ -142,10 +142,10 @@ const std::vector<uint8_t>& public_exponent) { DCHECK(blink::WebCryptoAlgorithm::isHash(hash_id)); return blink::WebCryptoAlgorithm::adoptParamsAndCreate( - algorithm_id, - new blink::WebCryptoRsaHashedKeyGenParams( - CreateAlgorithm(hash_id), modulus_length, - vector_as_array(&public_exponent), public_exponent.size())); + algorithm_id, new blink::WebCryptoRsaHashedKeyGenParams( + CreateAlgorithm(hash_id), modulus_length, + vector_as_array(&public_exponent), + static_cast<unsigned int>(public_exponent.size()))); } std::vector<uint8_t> Corrupted(const std::vector<uint8_t>& input) {
diff --git a/components/webcrypto/webcrypto.gyp b/components/webcrypto/webcrypto.gyp index 91b4476..f386343 100644 --- a/components/webcrypto/webcrypto.gyp +++ b/components/webcrypto/webcrypto.gyp
@@ -21,8 +21,6 @@ 'export_dependent_settings': [ '../../base/base.gyp:base', ], - # Disable c4267 warnings until we fix size_t to int truncations. - 'msvs_disabled_warnings': [ 4267, ], 'variables': { 'webcrypto_sources': [ 'algorithm_dispatch.cc',
diff --git a/components/webcrypto/webcrypto_impl.cc b/components/webcrypto/webcrypto_impl.cc index 6ea76539..3ccfbc9 100644 --- a/components/webcrypto/webcrypto_impl.cc +++ b/components/webcrypto/webcrypto_impl.cc
@@ -119,7 +119,8 @@ // theoretically this could overflow. CompleteWithError(Status::ErrorUnexpected(), result); } else { - result->completeWithBuffer(vector_as_array(&buffer), buffer.size()); + result->completeWithBuffer(vector_as_array(&buffer), + static_cast<unsigned int>(buffer.size())); } } } @@ -468,7 +469,7 @@ } else { state->result.completeWithJson( reinterpret_cast<const char*>(vector_as_array(&state->buffer)), - state->buffer.size()); + static_cast<unsigned int>(state->buffer.size())); } }
diff --git a/components/webcrypto/webcrypto_util.cc b/components/webcrypto/webcrypto_util.cc index edc3cd5..2e80c1f 100644 --- a/components/webcrypto/webcrypto_util.cc +++ b/components/webcrypto/webcrypto_util.cc
@@ -18,7 +18,7 @@ // Converts a (big-endian) WebCrypto BigInteger, with or without leading zeros, // to unsigned int. bool BigIntegerToUint(const uint8_t* data, - unsigned int data_size, + size_t data_size, unsigned int* result) { if (data_size == 0) return false;
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn index b8066cb..3ee0cac 100644 --- a/content/browser/BUILD.gn +++ b/content/browser/BUILD.gn
@@ -100,6 +100,7 @@ deps += [ "//cc", "//cc/surfaces", + "//components/scheduler:common", "//content/app/resources", "//content/app/strings", "//content/browser/devtools:resources", @@ -195,12 +196,6 @@ defines += [ "ENABLE_SCREEN_CAPTURE=1" ] deps += [ "//third_party/webrtc/modules/desktop_capture" ] } - if (is_linux || is_mac) { - sources += [ - "renderer_host/media/video_capture_texture_wrapper.cc", - "renderer_host/media/video_capture_texture_wrapper.h", - ] - } } if (is_win) {
diff --git a/content/browser/DEPS b/content/browser/DEPS index 56baa3f..35bad8b 100644 --- a/content/browser/DEPS +++ b/content/browser/DEPS
@@ -1,4 +1,9 @@ include_rules = [ + # Allow inclusion of specific components that we depend on. We may only + # depend on components which we share with the mojo html_viewer. + "+components/scheduler/common", + "+components/tracing", + "+content/app/strings/grit", # For generated headers "+content/public/browser", "+device/battery", # For battery status service. @@ -15,11 +20,6 @@ "+ui/webui", "+win8/util", - # TODO(joi): This was misplaced; need to move it somewhere else, - # since //content shouldn't depend on //components, which is a layer - # above. - "+components/tracing", - # In general, //content shouldn't depend on //device. # This is the an exception. "+device/udev_linux", # For udev utility and wrapper library.
diff --git a/content/browser/accessibility/accessibility_tree_formatter_mac.mm b/content/browser/accessibility/accessibility_tree_formatter_mac.mm index 7444327..7b99225 100644 --- a/content/browser/accessibility/accessibility_tree_formatter_mac.mm +++ b/content/browser/accessibility/accessibility_tree_formatter_mac.mm
@@ -161,6 +161,8 @@ @"AXARIABusy", @"AXARIALive", @"AXARIARelevant", + @"AXARIASetSize", + @"AXARIAPosInSet", NSAccessibilityColumnIndexRangeAttribute, @"AXDropEffects", NSAccessibilityEnabledAttribute,
diff --git a/content/browser/accessibility/browser_accessibility_android.cc b/content/browser/accessibility/browser_accessibility_android.cc index 9bfd68a3..dcb723fc5 100644 --- a/content/browser/accessibility/browser_accessibility_android.cc +++ b/content/browser/accessibility/browser_accessibility_android.cc
@@ -445,7 +445,7 @@ case ui::AX_ROLE_LIST_ITEM: case ui::AX_ROLE_LIST_BOX_OPTION: case ui::AX_ROLE_TREE_ITEM: - index = GetIndexInParent(); + index = GetIntAttribute(ui::AX_ATTR_POS_IN_SET) - 1; break; case ui::AX_ROLE_SLIDER: case ui::AX_ROLE_PROGRESS_INDICATOR: {
diff --git a/content/browser/accessibility/browser_accessibility_cocoa.mm b/content/browser/accessibility/browser_accessibility_cocoa.mm index 7db4fc7..89213b5 100644 --- a/content/browser/accessibility/browser_accessibility_cocoa.mm +++ b/content/browser/accessibility/browser_accessibility_cocoa.mm
@@ -108,6 +108,8 @@ { @"AXARIAAtomic", @"ariaAtomic" }, { @"AXARIABusy", @"ariaBusy" }, { @"AXARIALive", @"ariaLive" }, + { @"AXARIASetSize", @"ariaSetSize" }, + { @"AXARIAPosInSet", @"ariaPosInSet" }, { @"AXARIARelevant", @"ariaRelevant" }, { @"AXDropEffects", @"dropeffect" }, { @"AXGrabbed", @"grabbed" }, @@ -170,6 +172,16 @@ browserAccessibility_, ui::AX_ATTR_LIVE_RELEVANT); } +- (NSNumber*)ariaPosInSet { + return [NSNumber numberWithInt: + browserAccessibility_->GetIntAttribute(ui::AX_ATTR_POS_IN_SET)]; +} + +- (NSNumber*)ariaSetSize { + return [NSNumber numberWithInt: + browserAccessibility_->GetIntAttribute(ui::AX_ATTR_SET_SIZE)]; +} + // Returns an array of BrowserAccessibilityCocoa objects, representing the // accessibility children of this object. - (NSArray*)children { @@ -1407,6 +1419,18 @@ nil]]; } + // Position in set and Set size + if (browserAccessibility_->HasIntAttribute(ui::AX_ATTR_POS_IN_SET)) { + [ret addObjectsFromArray:[NSArray arrayWithObjects: + @"AXARIAPosInSet", + nil]]; + } + if (browserAccessibility_->HasIntAttribute(ui::AX_ATTR_SET_SIZE)) { + [ret addObjectsFromArray:[NSArray arrayWithObjects: + @"AXARIASetSize", + nil]]; + } + // Live regions. if (browserAccessibility_->HasStringAttribute( ui::AX_ATTR_LIVE_STATUS)) {
diff --git a/content/browser/accessibility/browser_accessibility_win.cc b/content/browser/accessibility/browser_accessibility_win.cc index e8a82f7..b61f2f9c2 100644 --- a/content/browser/accessibility/browser_accessibility_win.cc +++ b/content/browser/accessibility/browser_accessibility_win.cc
@@ -903,16 +903,10 @@ if (!group_level || !similar_items_in_group || !position_in_group) return E_INVALIDARG; - if (GetRole() == ui::AX_ROLE_LIST_BOX_OPTION && - GetParent() && - GetParent()->GetRole() == ui::AX_ROLE_LIST_BOX) { - *group_level = 0; - *similar_items_in_group = GetParent()->PlatformChildCount(); - *position_in_group = GetIndexInParent() + 1; - return S_OK; - } - - return E_NOTIMPL; + *group_level = 0; + *similar_items_in_group = GetIntAttribute(ui::AX_ATTR_SET_SIZE); + *position_in_group = GetIntAttribute(ui::AX_ATTR_POS_IN_SET); + return S_OK; } // @@ -3002,15 +2996,9 @@ // Expose "level" attribute for headings, trees, etc. IntAttributeToIA2(ui::AX_ATTR_HIERARCHICAL_LEVEL, "level"); - // Expose the set size and position in set for listbox options. - if (GetRole() == ui::AX_ROLE_LIST_BOX_OPTION && - GetParent() && - GetParent()->GetRole() == ui::AX_ROLE_LIST_BOX) { - win_attributes_->ia2_attributes.push_back( - L"setsize:" + base::IntToString16(GetParent()->PlatformChildCount())); - win_attributes_->ia2_attributes.push_back( - L"setsize:" + base::IntToString16(GetIndexInParent() + 1)); - } + // Expose the set size and position in set. + IntAttributeToIA2(ui::AX_ATTR_SET_SIZE, "setsize"); + IntAttributeToIA2(ui::AX_ATTR_POS_IN_SET, "posinset"); if (ia_role() == ROLE_SYSTEM_CHECKBUTTON || ia_role() == ROLE_SYSTEM_RADIOBUTTON ||
diff --git a/content/browser/accessibility/dump_accessibility_tree_browsertest.cc b/content/browser/accessibility/dump_accessibility_tree_browsertest.cc index f71fcd4..1057e4cd 100644 --- a/content/browser/accessibility/dump_accessibility_tree_browsertest.cc +++ b/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
@@ -413,6 +413,10 @@ RunAriaTest(FILE_PATH_LITERAL("aria-option.html")); } +IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityAriaPosinset) { + RunAriaTest(FILE_PATH_LITERAL("aria-posinset.html")); +} + IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityAriaPresentation) { RunAriaTest(FILE_PATH_LITERAL("aria-presentation.html")); @@ -484,6 +488,10 @@ RunAriaTest(FILE_PATH_LITERAL("aria-separator.html")); } +IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityAriaSetsize) { + RunAriaTest(FILE_PATH_LITERAL("aria-setsize.html")); +} + IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityAriaSlider) { RunAriaTest(FILE_PATH_LITERAL("aria-slider.html")); }
diff --git a/content/browser/appcache/appcache_update_job.cc b/content/browser/appcache/appcache_update_job.cc index b5d29a9..4b212861 100644 --- a/content/browser/appcache/appcache_update_job.cc +++ b/content/browser/appcache/appcache_update_job.cc
@@ -7,11 +7,12 @@ #include "base/bind.h" #include "base/bind_helpers.h" #include "base/compiler_specific.h" -#include "base/message_loop/message_loop.h" #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" +#include "base/thread_task_runner_handle.h" #include "content/browser/appcache/appcache_group.h" #include "content/browser/appcache/appcache_histograms.h" +#include "content/public/browser/browser_thread.h" #include "net/base/io_buffer.h" #include "net/base/load_flags.h" #include "net/base/net_errors.h" @@ -360,7 +361,8 @@ manifest_fetcher_(NULL), manifest_has_valid_mime_type_(false), stored_state_(UNSTORED), - storage_(service->storage()) { + storage_(service->storage()), + weak_factory_(this) { service_->AddObserver(this); } @@ -440,7 +442,10 @@ is_new_pending_master_entry); } - FetchManifest(true); + BrowserThread::PostAfterStartupTask( + FROM_HERE, base::ThreadTaskRunnerHandle::Get(), + base::Bind(&AppCacheUpdateJob::FetchManifest, weak_factory_.GetWeakPtr(), + true)); } AppCacheResponseWriter* AppCacheUpdateJob::CreateResponseWriter() {
diff --git a/content/browser/appcache/appcache_update_job.h b/content/browser/appcache/appcache_update_job.h index 4d2d896..8dfd153a 100644 --- a/content/browser/appcache/appcache_update_job.h +++ b/content/browser/appcache/appcache_update_job.h
@@ -13,6 +13,7 @@ #include "base/gtest_prod_util.h" #include "base/memory/ref_counted.h" +#include "base/memory/weak_ptr.h" #include "base/time/time.h" #include "content/browser/appcache/appcache.h" #include "content/browser/appcache/appcache_host.h" @@ -335,6 +336,7 @@ StoredState stored_state_; AppCacheStorage* storage_; + base::WeakPtrFactory<AppCacheUpdateJob> weak_factory_; FRIEND_TEST_ALL_PREFIXES(content::AppCacheGroupTest, QueueUpdate);
diff --git a/content/browser/appcache/appcache_update_job_unittest.cc b/content/browser/appcache/appcache_update_job_unittest.cc index 4955a71..b69af526 100644 --- a/content/browser/appcache/appcache_update_job_unittest.cc +++ b/content/browser/appcache/appcache_update_job_unittest.cc
@@ -724,9 +724,6 @@ MockFrontend* frontend = MakeMockFrontend(); AppCacheHost* host = MakeHost(1, frontend); update->StartUpdate(host, GURL()); - EXPECT_TRUE(update->manifest_fetcher_ != NULL); - - update->manifest_fetcher_->request()->CancelWithError(-100); // Set up checks for when update job finishes. do_checks_after_update_finished_ = true; @@ -757,9 +754,6 @@ host2->AssociateCompleteCache(cache); update->StartUpdate(NULL, GURL()); - EXPECT_TRUE(update->manifest_fetcher_ != NULL); - - update->manifest_fetcher_->request()->CancelWithError(-100); // Set up checks for when update job finishes. do_checks_after_update_finished_ = true; @@ -794,7 +788,6 @@ MockFrontend* frontend = MakeMockFrontend(); AppCacheHost* host = MakeHost(1, frontend); update->StartUpdate(host, GURL()); - EXPECT_TRUE(update->manifest_fetcher_ != NULL); // Set up checks for when update job finishes. do_checks_after_update_finished_ = true; @@ -826,7 +819,6 @@ frontend->SetVerifyProgressEvents(true); update->StartUpdate(NULL, GURL()); - EXPECT_TRUE(update->manifest_fetcher_ != NULL); // Set up checks for when update job finishes. do_checks_after_update_finished_ = true; @@ -864,7 +856,6 @@ host2->AssociateCompleteCache(cache); update->StartUpdate(NULL, GURL()); - EXPECT_TRUE(update->manifest_fetcher_ != NULL); // Set up checks for when update job finishes. do_checks_after_update_finished_ = true; @@ -895,7 +886,6 @@ MockFrontend* frontend = MakeMockFrontend(); AppCacheHost* host = MakeHost(1, frontend); update->StartUpdate(host, GURL()); - EXPECT_TRUE(update->manifest_fetcher_ != NULL); // Set up checks for when update job finishes. do_checks_after_update_finished_ = true; @@ -921,7 +911,6 @@ MockFrontend* frontend = MakeMockFrontend(); AppCacheHost* host = MakeHost(1, frontend); update->StartUpdate(host, GURL()); - EXPECT_TRUE(update->manifest_fetcher_ != NULL); // Set up checks for when update job finishes. do_checks_after_update_finished_ = true; @@ -953,7 +942,6 @@ host2->AssociateCompleteCache(cache); update->StartUpdate(NULL, GURL()); - EXPECT_TRUE(update->manifest_fetcher_ != NULL); // Set up checks for when update job finishes. do_checks_after_update_finished_ = true; @@ -1040,7 +1028,6 @@ host->AssociateCompleteCache(cache); update->StartUpdate(NULL, GURL()); - EXPECT_TRUE(update->manifest_fetcher_ != NULL); // Set up checks for when update job finishes. do_checks_after_update_finished_ = true; @@ -1061,7 +1048,6 @@ AppCacheUpdateJob* update = group_->update_job_; update->StartUpdate(NULL, GURL()); - EXPECT_TRUE(update->manifest_fetcher_ != NULL); WaitForUpdateToFinish(); } @@ -1391,7 +1377,6 @@ AppCacheEntry(AppCacheEntry::MASTER, 111)); update->StartUpdate(NULL, GURL()); - EXPECT_TRUE(update->manifest_fetcher_ != NULL); // Set up checks for when update job finishes. do_checks_after_update_finished_ = true; @@ -1431,7 +1416,6 @@ MockFrontend* frontend = MakeMockFrontend(); AppCacheHost* host = MakeHost(1, frontend); update->StartUpdate(host, GURL()); - EXPECT_TRUE(update->manifest_fetcher_ != NULL); // Set up checks for when update job finishes. do_checks_after_update_finished_ = true; @@ -1465,7 +1449,6 @@ host2->AssociateCompleteCache(cache); update->StartUpdate(NULL, GURL()); - EXPECT_TRUE(update->manifest_fetcher_ != NULL); // Set up checks for when update job finishes. do_checks_after_update_finished_ = true; @@ -1533,7 +1516,6 @@ MakeAppCacheResponseInfo(kManifestUrl, 555, kRawHeaders); update->StartUpdate(NULL, GURL()); - EXPECT_TRUE(update->manifest_fetcher_ != NULL); // Set up checks for when update job finishes. do_checks_after_update_finished_ = true; @@ -1602,7 +1584,6 @@ frontend1->SetVerifyProgressEvents(true); update->StartUpdate(NULL, GURL()); - EXPECT_TRUE(update->manifest_fetcher_ != NULL); // Set up checks for when update job finishes. do_checks_after_update_finished_ = true; @@ -1642,7 +1623,6 @@ frontend->SetVerifyProgressEvents(true); update->StartUpdate(host, GURL()); - EXPECT_TRUE(update->manifest_fetcher_ != NULL); // Set up checks for when update job finishes. do_checks_after_update_finished_ = true; @@ -1681,7 +1661,6 @@ MockFrontend* frontend = MakeMockFrontend(); AppCacheHost* host = MakeHost(1, frontend); update->StartUpdate(host, GURL()); - EXPECT_TRUE(update->manifest_fetcher_ != NULL); // Set up checks for when update job finishes. do_checks_after_update_finished_ = true; @@ -1715,7 +1694,6 @@ MockFrontend* frontend = MakeMockFrontend(); AppCacheHost* host = MakeHost(1, frontend); update->StartUpdate(host, GURL()); - EXPECT_TRUE(update->manifest_fetcher_ != NULL); // Set up checks for when update job finishes. do_checks_after_update_finished_ = true; @@ -1750,7 +1728,6 @@ MockFrontend* frontend = MakeMockFrontend(); AppCacheHost* host = MakeHost(1, frontend); update->StartUpdate(host, GURL()); - EXPECT_TRUE(update->manifest_fetcher_ != NULL); // Set up checks for when update job finishes. do_checks_after_update_finished_ = true; @@ -1784,7 +1761,6 @@ MockFrontend* frontend = MakeMockFrontend(); AppCacheHost* host = MakeHost(1, frontend); update->StartUpdate(host, GURL()); - EXPECT_TRUE(update->manifest_fetcher_ != NULL); // Set up checks for when update job finishes. do_checks_after_update_finished_ = true; @@ -1817,7 +1793,6 @@ MockFrontend* frontend = MakeMockFrontend(); AppCacheHost* host = MakeHost(1, frontend); update->StartUpdate(host, GURL()); - EXPECT_TRUE(update->manifest_fetcher_ != NULL); // Set up checks for when update job finishes. do_checks_after_update_finished_ = true; @@ -1978,7 +1953,6 @@ host2->AssociateCompleteCache(cache); update->StartUpdate(NULL, GURL()); - EXPECT_TRUE(update->manifest_fetcher_ != NULL); // Set up checks for when update job finishes. do_checks_after_update_finished_ = true; @@ -2008,9 +1982,6 @@ AppCacheHost* host = MakeHost(1, frontend); host->new_master_entry_url_ = GURL("http://failme/blah"); update->StartUpdate(host, host->new_master_entry_url_); - EXPECT_TRUE(update->manifest_fetcher_ != NULL); - - update->manifest_fetcher_->request()->CancelWithError(-100); // Set up checks for when update job finishes. do_checks_after_update_finished_ = true; @@ -2037,7 +2008,6 @@ AppCacheHost* host = MakeHost(1, frontend); host->new_master_entry_url_ = MockHttpServer::GetMockUrl("files/blah"); update->StartUpdate(host, host->new_master_entry_url_); - EXPECT_TRUE(update->manifest_fetcher_ != NULL); // Set up checks for when update job finishes. do_checks_after_update_finished_ = true; @@ -2067,7 +2037,6 @@ host->new_master_entry_url_ = MockHttpServer::GetMockUrl("files/blah"); update->StartUpdate(host, host->new_master_entry_url_); - EXPECT_TRUE(update->manifest_fetcher_ != NULL); // Set up checks for when update job finishes. do_checks_after_update_finished_ = true; @@ -2097,7 +2066,6 @@ MockHttpServer::GetMockUrl("files/explicit1"); update->StartUpdate(host, host->new_master_entry_url_); - EXPECT_TRUE(update->manifest_fetcher_ != NULL); // Set up checks for when update job finishes. do_checks_after_update_finished_ = true; @@ -2386,7 +2354,6 @@ host1->new_master_entry_url_ = MockHttpServer::GetMockUrl("files/explicit2"); update->StartUpdate(host1, host1->new_master_entry_url_); - EXPECT_TRUE(update->manifest_fetcher_ != NULL); // Set up additional updates to be started while update is in progress. MockFrontend* frontend2 = MakeMockFrontend(); @@ -2484,7 +2451,6 @@ host2->new_master_entry_url_ = MockHttpServer::GetMockUrl("files/nosuchfile"); update->StartUpdate(host2, host2->new_master_entry_url_); - EXPECT_TRUE(update->manifest_fetcher_ != NULL); // Set up additional updates to be started while update is in progress. MockFrontend* frontend3 = MakeMockFrontend();
diff --git a/content/browser/background_sync/background_sync.proto b/content/browser/background_sync/background_sync.proto index b5f2e094..895117d4 100644 --- a/content/browser/background_sync/background_sync.proto +++ b/content/browser/background_sync/background_sync.proto
@@ -36,4 +36,5 @@ message BackgroundSyncRegistrationsProto { repeated BackgroundSyncRegistrationProto registration = 1; required int64 next_registration_id = 2; + required string origin = 3; } \ No newline at end of file
diff --git a/content/browser/background_sync/background_sync_manager.cc b/content/browser/background_sync/background_sync_manager.cc index 4822464d..a23792f1 100644 --- a/content/browser/background_sync/background_sync_manager.cc +++ b/content/browser/background_sync/background_sync_manager.cc
@@ -28,10 +28,7 @@ BackgroundSyncRegistrations() : next_id(BackgroundSyncRegistration::kInitialId) { } -BackgroundSyncManager::BackgroundSyncRegistrations::BackgroundSyncRegistrations( - BackgroundSyncRegistration::RegistrationId next_id) - : next_id(next_id) { -} + BackgroundSyncManager::BackgroundSyncRegistrations:: ~BackgroundSyncRegistrations() { } @@ -206,10 +203,10 @@ for (const std::pair<int64, std::string>& data : user_data) { BackgroundSyncRegistrationsProto registrations_proto; if (registrations_proto.ParseFromString(data.second)) { - sw_to_registrations_map_[data.first] = BackgroundSyncRegistrations( - registrations_proto.next_registration_id()); BackgroundSyncRegistrations* registrations = &sw_to_registrations_map_[data.first]; + registrations->next_id = registrations_proto.next_registration_id(); + registrations->origin = GURL(registrations_proto.origin()); for (int i = 0, max = registrations_proto.registration_size(); i < max; ++i) { @@ -262,15 +259,13 @@ return; } - BackgroundSyncRegistration existing_registration; - if (LookupRegistration(sw_registration_id, RegistrationKey(sync_registration), - &existing_registration)) { - if (existing_registration.Equals(sync_registration)) { - base::MessageLoop::current()->PostTask( - FROM_HERE, - base::Bind(callback, ERROR_TYPE_OK, existing_registration)); - return; - } + const BackgroundSyncRegistration* existing_registration = LookupRegistration( + sw_registration_id, RegistrationKey(sync_registration)); + if (existing_registration && + existing_registration->Equals(sync_registration)) { + base::MessageLoop::current()->PostTask( + FROM_HERE, base::Bind(callback, ERROR_TYPE_OK, *existing_registration)); + return; } BackgroundSyncRegistration new_registration = sync_registration; @@ -278,10 +273,10 @@ &sw_to_registrations_map_[sw_registration_id]; new_registration.id = registrations->next_id++; - AddRegistrationToMap(sw_registration_id, new_registration); + AddRegistrationToMap(sw_registration_id, origin, new_registration); StoreRegistrations( - origin, sw_registration_id, + sw_registration_id, base::Bind(&BackgroundSyncManager::RegisterDidStore, weak_ptr_factory_.GetWeakPtr(), sw_registration_id, new_registration, callback)); @@ -319,7 +314,7 @@ base::BarrierClosure(user_data.size(), base::Bind(callback)); for (const auto& sw_id_and_regs : user_data) { - service_worker_context_->context()->storage()->ClearUserData( + service_worker_context_->ClearRegistrationUserData( sw_id_and_regs.first, kBackgroundSyncUserDataKey, base::Bind(&BackgroundSyncManager::DisableAndClearManagerClearedOne, weak_ptr_factory_.GetWeakPtr(), barrier_closure)); @@ -334,29 +329,28 @@ base::Bind(barrier_closure)); } -bool BackgroundSyncManager::LookupRegistration( +BackgroundSyncManager::BackgroundSyncRegistration* +BackgroundSyncManager::LookupRegistration( int64 sw_registration_id, - const RegistrationKey& registration_key, - BackgroundSyncRegistration* existing_registration) { + const RegistrationKey& registration_key) { SWIdToRegistrationsMap::iterator it = sw_to_registrations_map_.find(sw_registration_id); if (it == sw_to_registrations_map_.end()) - return false; + return nullptr; - const BackgroundSyncRegistrations& registrations = it->second; - const auto key_and_registration_iter = + BackgroundSyncRegistrations& registrations = it->second; + DCHECK_LE(BackgroundSyncRegistration::kInitialId, registrations.next_id); + DCHECK(!registrations.origin.is_empty()); + + auto key_and_registration_iter = registrations.registration_map.find(registration_key); if (key_and_registration_iter == registrations.registration_map.end()) - return false; + return nullptr; - if (existing_registration) - *existing_registration = key_and_registration_iter->second; - - return true; + return &key_and_registration_iter->second; } void BackgroundSyncManager::StoreRegistrations( - const GURL& origin, int64 sw_registration_id, const ServiceWorkerStorage::StatusCallback& callback) { // Serialize the data. @@ -364,6 +358,7 @@ sw_to_registrations_map_[sw_registration_id]; BackgroundSyncRegistrationsProto registrations_proto; registrations_proto.set_next_registration_id(registrations.next_id); + registrations_proto.set_origin(registrations.origin.spec()); for (const auto& key_and_registration : registrations.registration_map) { const BackgroundSyncRegistration& registration = @@ -381,8 +376,8 @@ bool success = registrations_proto.SerializeToString(&serialized); DCHECK(success); - StoreDataInBackend(sw_registration_id, origin, kBackgroundSyncUserDataKey, - serialized, callback); + StoreDataInBackend(sw_registration_id, registrations.origin, + kBackgroundSyncUserDataKey, serialized, callback); } void BackgroundSyncManager::RegisterDidStore( @@ -415,7 +410,7 @@ void BackgroundSyncManager::RemoveRegistrationFromMap( int64 sw_registration_id, const RegistrationKey& registration_key) { - DCHECK(LookupRegistration(sw_registration_id, registration_key, nullptr)); + DCHECK(LookupRegistration(sw_registration_id, registration_key)); BackgroundSyncRegistrations* registrations = &sw_to_registrations_map_[sw_registration_id]; @@ -425,12 +420,14 @@ void BackgroundSyncManager::AddRegistrationToMap( int64 sw_registration_id, + const GURL& origin, const BackgroundSyncRegistration& sync_registration) { DCHECK_NE(BackgroundSyncRegistration::kInvalidRegistrationId, sw_registration_id); BackgroundSyncRegistrations* registrations = &sw_to_registrations_map_[sw_registration_id]; + registrations->origin = origin; RegistrationKey registration_key(sync_registration); registrations->registration_map[registration_key] = sync_registration; @@ -442,7 +439,7 @@ const std::string& backend_key, const std::string& data, const ServiceWorkerStorage::StatusCallback& callback) { - service_worker_context_->context()->storage()->StoreUserData( + service_worker_context_->StoreRegistrationUserData( sw_registration_id, origin, backend_key, data, callback); } @@ -452,8 +449,8 @@ callback) { DCHECK_CURRENTLY_ON(BrowserThread::IO); - service_worker_context_->context()->storage()->GetUserDataForAllRegistrations( - backend_key, callback); + service_worker_context_->GetUserDataForAllRegistrations(backend_key, + callback); } void BackgroundSyncManager::UnregisterImpl( @@ -468,10 +465,10 @@ return; } - BackgroundSyncRegistration existing_registration; - if (!LookupRegistration(sw_registration_id, registration_key, - &existing_registration) || - existing_registration.id != sync_registration_id) { + const BackgroundSyncRegistration* existing_registration = + LookupRegistration(sw_registration_id, registration_key); + if (!existing_registration || + existing_registration->id != sync_registration_id) { base::MessageLoop::current()->PostTask( FROM_HERE, base::Bind(callback, ERROR_TYPE_NOT_FOUND)); return; @@ -480,7 +477,7 @@ RemoveRegistrationFromMap(sw_registration_id, registration_key); StoreRegistrations( - origin, sw_registration_id, + sw_registration_id, base::Bind(&BackgroundSyncManager::UnregisterDidStore, weak_ptr_factory_.GetWeakPtr(), sw_registration_id, callback)); } @@ -520,9 +517,9 @@ return; } - BackgroundSyncRegistration out_registration; - if (!LookupRegistration(sw_registration_id, registration_key, - &out_registration)) { + const BackgroundSyncRegistration* out_registration = + LookupRegistration(sw_registration_id, registration_key); + if (!out_registration) { base::MessageLoop::current()->PostTask( FROM_HERE, base::Bind(callback, ERROR_TYPE_NOT_FOUND, BackgroundSyncRegistration())); @@ -530,7 +527,7 @@ } base::MessageLoop::current()->PostTask( - FROM_HERE, base::Bind(callback, ERROR_TYPE_OK, out_registration)); + FROM_HERE, base::Bind(callback, ERROR_TYPE_OK, *out_registration)); } void BackgroundSyncManager::OnRegistrationDeletedImpl(
diff --git a/content/browser/background_sync/background_sync_manager.h b/content/browser/background_sync/background_sync_manager.h index b606a451..a4d7714 100644 --- a/content/browser/background_sync/background_sync_manager.h +++ b/content/browser/background_sync/background_sync_manager.h
@@ -56,7 +56,7 @@ static const RegistrationId kInitialId; BackgroundSyncRegistration() {} - bool Equals(const BackgroundSyncRegistration& other) { + bool Equals(const BackgroundSyncRegistration& other) const { return this->tag == other.tag && this->periodicity == other.periodicity && this->min_period == other.min_period && network_state == other.network_state && @@ -157,12 +157,11 @@ std::map<RegistrationKey, BackgroundSyncRegistration>; BackgroundSyncRegistrations(); - explicit BackgroundSyncRegistrations( - BackgroundSyncRegistration::RegistrationId next_id); ~BackgroundSyncRegistrations(); RegistrationMap registration_map; BackgroundSyncRegistration::RegistrationId next_id; + GURL origin; }; using PermissionStatusCallback = base::Callback<void(bool)>; @@ -183,13 +182,12 @@ // Returns the existing registration in |existing_registration| if it is not // null. - bool LookupRegistration(int64 sw_registration_id, - const RegistrationKey& registration_key, - BackgroundSyncRegistration* existing_registration); + BackgroundSyncRegistration* LookupRegistration( + int64 sw_registration_id, + const RegistrationKey& registration_key); // Store all registrations for a given |sw_registration_id|. - void StoreRegistrations(const GURL& origin, - int64 sw_registration_id, + void StoreRegistrations(int64 sw_registration_id, const ServiceWorkerStorage::StatusCallback& callback); // Removes the registration if it is in the map. @@ -198,6 +196,7 @@ void AddRegistrationToMap( int64 sw_registration_id, + const GURL& origin, const BackgroundSyncRegistration& sync_registration); void InitImpl(const base::Closure& callback);
diff --git a/content/browser/bluetooth/bluetooth_dispatcher_host.cc b/content/browser/bluetooth/bluetooth_dispatcher_host.cc index f07846c..fb62188 100644 --- a/content/browser/bluetooth/bluetooth_dispatcher_host.cc +++ b/content/browser/bluetooth/bluetooth_dispatcher_host.cc
@@ -31,6 +31,10 @@ return host; } +void BluetoothDispatcherHost::OnDestruct() const { + BrowserThread::DeleteOnUIThread::Destruct(this); +} + bool BluetoothDispatcherHost::OnMessageReceived(const IPC::Message& message) { DCHECK_CURRENTLY_ON(BrowserThread::IO); bool handled = true; @@ -52,12 +56,14 @@ } BluetoothDispatcherHost::~BluetoothDispatcherHost() { + DCHECK_CURRENTLY_ON(BrowserThread::UI); // Clear adapter, releasing observer references. set_adapter(scoped_refptr<device::BluetoothAdapter>()); } void BluetoothDispatcherHost::set_adapter( scoped_refptr<device::BluetoothAdapter> adapter) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); if (adapter_.get()) adapter_->RemoveObserver(this); adapter_ = adapter;
diff --git a/content/browser/bluetooth/bluetooth_dispatcher_host.h b/content/browser/bluetooth/bluetooth_dispatcher_host.h index 2d00d23..a49c759 100644 --- a/content/browser/bluetooth/bluetooth_dispatcher_host.h +++ b/content/browser/bluetooth/bluetooth_dispatcher_host.h
@@ -18,6 +18,10 @@ // Intended to be instantiated by the RenderProcessHost and installed as // a filter on the channel. BrowserMessageFilter is refcounted and typically // lives as long as it is installed on a channel. +// +// BluetoothDispatcherHost primarily operates on the UI thread because the +// BluetoothAdapter and related objects live there. An exception is made for +// Receiving IPC on the IO thread. class BluetoothDispatcherHost : public BrowserMessageFilter, public device::BluetoothAdapter::Observer { public: @@ -25,6 +29,7 @@ static scoped_refptr<BluetoothDispatcherHost> Create(); // BrowserMessageFilter: + void OnDestruct() const override; bool OnMessageReceived(const IPC::Message& message) override; protected: @@ -32,6 +37,9 @@ ~BluetoothDispatcherHost() override; private: + friend class base::DeleteHelper<BluetoothDispatcherHost>; + friend struct BrowserThread::DeleteOnThread<BrowserThread::UI>; + // Set |adapter_| to a BluetoothAdapter instance and register observers, // releasing references to previous |adapter_|. void set_adapter(scoped_refptr<device::BluetoothAdapter> adapter);
diff --git a/content/browser/browser_main_loop.cc b/content/browser/browser_main_loop.cc index d485389..3dfc8bd 100644 --- a/content/browser/browser_main_loop.cc +++ b/content/browser/browser_main_loop.cc
@@ -20,6 +20,7 @@ #include "base/thread_task_runner_handle.h" #include "base/threading/thread_restrictions.h" #include "base/timer/hi_res_timer_manager.h" +#include "base/trace_event/memory_dump_manager.h" #include "base/trace_event/trace_event.h" #include "content/browser/browser_thread_impl.h" #include "content/browser/device_sensors/device_inertial_sensor_service.h" @@ -573,6 +574,8 @@ base::MessageLoop::current()->AddTaskObserver(memory_observer_.get()); } + base::trace_event::MemoryDumpManager::GetInstance()->Initialize(); + #if defined(TCMALLOC_TRACE_MEMORY_SUPPORTED) trace_memory_controller_.reset(new base::trace_event::TraceMemoryController( base::MessageLoop::current()->message_loop_proxy(),
diff --git a/content/browser/compositor/browser_compositor_output_surface.h b/content/browser/compositor/browser_compositor_output_surface.h index fd15e03d..615a54e3 100644 --- a/content/browser/compositor/browser_compositor_output_surface.h +++ b/content/browser/compositor/browser_compositor_output_surface.h
@@ -41,8 +41,8 @@ #if defined(OS_MACOSX) virtual void OnSurfaceDisplayed() = 0; - virtual void OnSurfaceRecycled() = 0; - virtual bool ShouldNotShowFramesAfterRecycle() const = 0; + virtual void SetSurfaceSuspendedForRecycle(bool suspended) = 0; + virtual bool SurfaceShouldNotShowFramesAfterSuspendForRecycle() const = 0; #endif protected:
diff --git a/content/browser/compositor/browser_compositor_view_mac.h b/content/browser/compositor/browser_compositor_view_mac.h index 41e8bc2..28b824e 100644 --- a/content/browser/compositor/browser_compositor_view_mac.h +++ b/content/browser/compositor/browser_compositor_view_mac.h
@@ -7,6 +7,7 @@ #include "ui/accelerated_widget_mac/accelerated_widget_mac.h" #include "ui/compositor/compositor.h" +#include "ui/compositor/compositor_observer.h" namespace content { @@ -14,9 +15,9 @@ // into. This structure is used to efficiently recycle these structures across // tabs (because creating a new ui::Compositor for each tab would be expensive // in terms of time and resources). -class BrowserCompositorMac { +class BrowserCompositorMac : public ui::CompositorObserver { public: - ~BrowserCompositorMac(); + virtual ~BrowserCompositorMac(); // Create a compositor, or recycle a preexisting one. static scoped_ptr<BrowserCompositorMac> Create(); @@ -42,6 +43,15 @@ private: BrowserCompositorMac(); + // ui::CompositorObserver implementation: + void OnCompositingDidCommit(ui::Compositor* compositor) override; + void OnCompositingStarted(ui::Compositor* compositor, + base::TimeTicks start_time) override {} + void OnCompositingEnded(ui::Compositor* compositor) override {} + void OnCompositingAborted(ui::Compositor* compositor) override {} + void OnCompositingLockStateChanged(ui::Compositor* compositor) override {} + void OnCompositingShuttingDown(ui::Compositor* compositor) override {} + scoped_ptr<ui::AcceleratedWidgetMac> accelerated_widget_mac_; ui::Compositor compositor_; scoped_refptr<ui::CompositorLock> compositor_suspended_lock_;
diff --git a/content/browser/compositor/browser_compositor_view_mac.mm b/content/browser/compositor/browser_compositor_view_mac.mm index 322183d0..472c17fa 100644 --- a/content/browser/compositor/browser_compositor_view_mac.mm +++ b/content/browser/compositor/browser_compositor_view_mac.mm
@@ -48,9 +48,12 @@ RenderWidgetResizeHelper::Get()->task_runner()) { compositor_.SetLocksWillTimeOut(false); Suspend(); + compositor_.AddObserver(this); } -BrowserCompositorMac::~BrowserCompositorMac() {} +BrowserCompositorMac::~BrowserCompositorMac() { + compositor_.RemoveObserver(this); +} void BrowserCompositorMac::Suspend() { compositor_suspended_lock_ = compositor_.GetCompositorLock(); @@ -60,6 +63,13 @@ compositor_suspended_lock_ = nullptr; } +void BrowserCompositorMac::OnCompositingDidCommit( + ui::Compositor* compositor_that_did_commit) { + DCHECK_EQ(compositor_that_did_commit, compositor()); + content::ImageTransportFactory::GetInstance() + ->SetCompositorSuspendedForRecycle(compositor(), false); +} + // static scoped_ptr<BrowserCompositorMac> BrowserCompositorMac::Create() { if (g_recyclable_browser_compositor.Get()) @@ -71,8 +81,8 @@ void BrowserCompositorMac::Recycle( scoped_ptr<BrowserCompositorMac> compositor) { DCHECK(compositor); - content::ImageTransportFactory::GetInstance()->OnCompositorRecycled( - compositor->compositor()); + content::ImageTransportFactory::GetInstance() + ->SetCompositorSuspendedForRecycle(compositor->compositor(), true); // It is an error to have a browser compositor continue to exist after // shutdown.
diff --git a/content/browser/compositor/gpu_browser_compositor_output_surface.cc b/content/browser/compositor/gpu_browser_compositor_output_surface.cc index b15d5c21..09a8b7280 100644 --- a/content/browser/compositor/gpu_browser_compositor_output_surface.cc +++ b/content/browser/compositor/gpu_browser_compositor_output_surface.cc
@@ -25,7 +25,7 @@ vsync_manager, overlay_candidate_validator.Pass()), #if defined(OS_MACOSX) - should_not_show_frames_(false), + should_show_frames_state_(SHOULD_SHOW_FRAMES), #endif swap_buffers_completion_callback_( base::Bind(&GpuBrowserCompositorOutputSurface::OnSwapBuffersCompleted, @@ -85,8 +85,10 @@ client_->DidSwapBuffers(); #if defined(OS_MACOSX) - if (should_not_show_frames_) - should_not_show_frames_ = false; + if (should_show_frames_state_ == + SHOULD_NOT_SHOW_FRAMES_NO_SWAP_AFTER_SUSPENDED) { + should_show_frames_state_ = SHOULD_SHOW_FRAMES; + } #endif } @@ -114,25 +116,34 @@ cc::OutputSurface::OnSwapBuffersComplete(); } -void GpuBrowserCompositorOutputSurface::OnSurfaceRecycled() { - // Discard the backbuffer immediately. This is necessary only when using a - // ImageTransportSurfaceFBO with a CALayerStorageProvider. Discarding the - // backbuffer results in the next frame using a new CALayer and CAContext, - // which guarantees that the browser will not flash stale content when adding - // the remote CALayer to the NSView hierarchy (it could flash stale content - // because the system window server is not synchronized with any signals we - // control or observe). - DiscardBackbuffer(); - // It may be that there are frames in-flight from the GPU process back to the - // browser. Make sure that these frames are not displayed by ignoring them in - // GpuProcessHostUIShim, until the browser issues a SwapBuffers for the new - // content. - should_not_show_frames_ = true; +void GpuBrowserCompositorOutputSurface::SetSurfaceSuspendedForRecycle( + bool suspended) { + if (suspended) { + // It may be that there are frames in-flight from the GPU process back to + // the browser. Make sure that these frames are not displayed by ignoring + // them in GpuProcessHostUIShim, until the browser issues a SwapBuffers for + // the new content. + should_show_frames_state_ = SHOULD_NOT_SHOW_FRAMES_SUSPENDED; + } else { + // Discard the backbuffer before drawing the new frame. This is necessary + // only when using a ImageTransportSurfaceFBO with a + // CALayerStorageProvider. Discarding the backbuffer results in the next + // frame using a new CALayer and CAContext, which guarantees that the + // browser will not flash stale content when adding the remote CALayer to + // the NSView hierarchy (it could flash stale content because the system + // window server is not synchronized with any signals we control or + // observe). + if (should_show_frames_state_ == SHOULD_NOT_SHOW_FRAMES_SUSPENDED) { + DiscardBackbuffer(); + should_show_frames_state_ = + SHOULD_NOT_SHOW_FRAMES_NO_SWAP_AFTER_SUSPENDED; + } + } } -bool GpuBrowserCompositorOutputSurface::ShouldNotShowFramesAfterRecycle() - const { - return should_not_show_frames_; +bool GpuBrowserCompositorOutputSurface:: + SurfaceShouldNotShowFramesAfterSuspendForRecycle() const { + return should_show_frames_state_ != SHOULD_SHOW_FRAMES; } #endif
diff --git a/content/browser/compositor/gpu_browser_compositor_output_surface.h b/content/browser/compositor/gpu_browser_compositor_output_surface.h index b5a5811..2b3d2b7 100644 --- a/content/browser/compositor/gpu_browser_compositor_output_surface.h +++ b/content/browser/compositor/gpu_browser_compositor_output_surface.h
@@ -37,9 +37,20 @@ #if defined(OS_MACOSX) void OnSurfaceDisplayed() override; - void OnSurfaceRecycled() override; - bool ShouldNotShowFramesAfterRecycle() const override; - bool should_not_show_frames_; + void SetSurfaceSuspendedForRecycle(bool suspended) override; + bool SurfaceShouldNotShowFramesAfterSuspendForRecycle() const override; + enum ShouldShowFramesState { + // Frames that come from the GPU process should appear on-screen. + SHOULD_SHOW_FRAMES, + // The compositor has been suspended. Any frames that come from the GPU + // process are for the pre-suspend content and should not be displayed. + SHOULD_NOT_SHOW_FRAMES_SUSPENDED, + // The compositor has been un-suspended, but has not yet issued a swap + // since being un-suspended, so any frames that come from the GPU process + // are for pre-suspend content and should not be displayed. + SHOULD_NOT_SHOW_FRAMES_NO_SWAP_AFTER_SUSPENDED, + }; + ShouldShowFramesState should_show_frames_state_; #endif CommandBufferProxyImpl* GetCommandBufferProxy();
diff --git a/content/browser/compositor/gpu_process_transport_factory.cc b/content/browser/compositor/gpu_process_transport_factory.cc index eff2349..f2d1b39 100644 --- a/content/browser/compositor/gpu_process_transport_factory.cc +++ b/content/browser/compositor/gpu_process_transport_factory.cc
@@ -469,8 +469,9 @@ surface->OnSurfaceDisplayed(); } -void GpuProcessTransportFactory::OnCompositorRecycled( - ui::Compositor* compositor) { +void GpuProcessTransportFactory::SetCompositorSuspendedForRecycle( + ui::Compositor* compositor, + bool suspended) { PerCompositorDataMap::iterator it = per_compositor_data_.find(compositor); if (it == per_compositor_data_.end()) return; @@ -479,15 +480,15 @@ BrowserCompositorOutputSurface* surface = output_surface_map_.Lookup(data->surface_id); if (surface) - surface->OnSurfaceRecycled(); + surface->SetSurfaceSuspendedForRecycle(suspended); } -bool GpuProcessTransportFactory::SurfaceShouldNotShowFramesAfterRecycle( - int surface_id) const { +bool GpuProcessTransportFactory:: + SurfaceShouldNotShowFramesAfterSuspendForRecycle(int surface_id) const { BrowserCompositorOutputSurface* surface = output_surface_map_.Lookup(surface_id); if (surface) - return surface->ShouldNotShowFramesAfterRecycle(); + return surface->SurfaceShouldNotShowFramesAfterSuspendForRecycle(); return false; } #endif
diff --git a/content/browser/compositor/gpu_process_transport_factory.h b/content/browser/compositor/gpu_process_transport_factory.h index 7ed1af6..54f02ef 100644 --- a/content/browser/compositor/gpu_process_transport_factory.h +++ b/content/browser/compositor/gpu_process_transport_factory.h
@@ -68,8 +68,10 @@ void RemoveObserver(ImageTransportFactoryObserver* observer) override; #if defined(OS_MACOSX) void OnSurfaceDisplayed(int surface_id) override; - void OnCompositorRecycled(ui::Compositor* compositor) override; - bool SurfaceShouldNotShowFramesAfterRecycle(int surface_id) const override; + void SetCompositorSuspendedForRecycle(ui::Compositor* compositor, + bool suspended) override; + bool SurfaceShouldNotShowFramesAfterSuspendForRecycle( + int surface_id) const override; #endif private:
diff --git a/content/browser/compositor/image_transport_factory.h b/content/browser/compositor/image_transport_factory.h index 5142f24..e9aa039 100644 --- a/content/browser/compositor/image_transport_factory.h +++ b/content/browser/compositor/image_transport_factory.h
@@ -85,13 +85,18 @@ #if defined(OS_MACOSX) virtual void OnSurfaceDisplayed(int surface_id) = 0; - // Called when the ui::Compositor has been disconnected from an NSView and - // may be attached to another one. This ensures that content and frames - // intended for the old NSView will not appear in the new NSView. - virtual void OnCompositorRecycled(ui::Compositor* compositor) = 0; + // Called with |suspended| as true when the ui::Compositor has been + // disconnected from an NSView and may be attached to another one. Called + // with |suspended| as false after the ui::Compositor has been connected to + // a new NSView and the first commit targeted at the new NSView has + // completed. This ensures that content and frames intended for the old + // NSView will not flash in the new NSView. + virtual void SetCompositorSuspendedForRecycle(ui::Compositor* compositor, + bool suspended) = 0; // Used by GpuProcessHostUIShim to determine if a frame should not be // displayed because it is targetted to an NSView that has been disconnected. - virtual bool SurfaceShouldNotShowFramesAfterRecycle(int surface_id) const = 0; + virtual bool SurfaceShouldNotShowFramesAfterSuspendForRecycle( + int surface_id) const = 0; #endif };
diff --git a/content/browser/compositor/reflector_impl_unittest.cc b/content/browser/compositor/reflector_impl_unittest.cc index a8de2d2..2f0c299 100644 --- a/content/browser/compositor/reflector_impl_unittest.cc +++ b/content/browser/compositor/reflector_impl_unittest.cc
@@ -82,8 +82,10 @@ #if defined(OS_MACOSX) void OnSurfaceDisplayed() override {} - void OnSurfaceRecycled() override {} - bool ShouldNotShowFramesAfterRecycle() const override { return false; } + void SetSurfaceSuspendedForRecycle(bool suspended) override {} + bool SurfaceShouldNotShowFramesAfterSuspendForRecycle() const override { + return false; + } #endif gfx::Size SurfaceSize() const override { return gfx::Size(256, 256); }
diff --git a/content/browser/compositor/software_browser_compositor_output_surface.cc b/content/browser/compositor/software_browser_compositor_output_surface.cc index 2d422c9..c2b70d4 100644 --- a/content/browser/compositor/software_browser_compositor_output_surface.cc +++ b/content/browser/compositor/software_browser_compositor_output_surface.cc
@@ -60,11 +60,12 @@ NOTREACHED() << "Not expected for software surfaces."; } -void SoftwareBrowserCompositorOutputSurface::OnSurfaceRecycled() { +void SoftwareBrowserCompositorOutputSurface::SetSurfaceSuspendedForRecycle( + bool suspended) { } -bool SoftwareBrowserCompositorOutputSurface::ShouldNotShowFramesAfterRecycle() - const { +bool SoftwareBrowserCompositorOutputSurface:: + SurfaceShouldNotShowFramesAfterSuspendForRecycle() const { return false; } #endif
diff --git a/content/browser/compositor/software_browser_compositor_output_surface.h b/content/browser/compositor/software_browser_compositor_output_surface.h index 245e682..68776ca 100644 --- a/content/browser/compositor/software_browser_compositor_output_surface.h +++ b/content/browser/compositor/software_browser_compositor_output_surface.h
@@ -33,8 +33,8 @@ #if defined(OS_MACOSX) void OnSurfaceDisplayed() override; - void OnSurfaceRecycled() override; - bool ShouldNotShowFramesAfterRecycle() const override; + void SetSurfaceSuspendedForRecycle(bool suspended) override; + bool SurfaceShouldNotShowFramesAfterSuspendForRecycle() const override; #endif base::WeakPtrFactory<SoftwareBrowserCompositorOutputSurface> weak_factory_;
diff --git a/content/browser/compositor/test/no_transport_image_transport_factory.cc b/content/browser/compositor/test/no_transport_image_transport_factory.cc index baebdef0..ff56b3f3 100644 --- a/content/browser/compositor/test/no_transport_image_transport_factory.cc +++ b/content/browser/compositor/test/no_transport_image_transport_factory.cc
@@ -61,8 +61,8 @@ } #if defined(OS_MACOSX) -bool NoTransportImageTransportFactory::SurfaceShouldNotShowFramesAfterRecycle( - int surface_id) const { +bool NoTransportImageTransportFactory:: + SurfaceShouldNotShowFramesAfterSuspendForRecycle(int surface_id) const { return false; } #endif
diff --git a/content/browser/compositor/test/no_transport_image_transport_factory.h b/content/browser/compositor/test/no_transport_image_transport_factory.h index d97fb01..e67a09df 100644 --- a/content/browser/compositor/test/no_transport_image_transport_factory.h +++ b/content/browser/compositor/test/no_transport_image_transport_factory.h
@@ -30,8 +30,10 @@ void RemoveObserver(ImageTransportFactoryObserver* observer) override; #if defined(OS_MACOSX) void OnSurfaceDisplayed(int surface_id) override {} - void OnCompositorRecycled(ui::Compositor* compositor) override {} - bool SurfaceShouldNotShowFramesAfterRecycle(int surface_id) const override; + void SetCompositorSuspendedForRecycle(ui::Compositor* compositor, + bool suspended) override {} + bool SurfaceShouldNotShowFramesAfterSuspendForRecycle( + int surface_id) const override; #endif private:
diff --git a/content/browser/devtools/devtools_agent_host_impl.cc b/content/browser/devtools/devtools_agent_host_impl.cc index 3eb6acec..cba575e 100644 --- a/content/browser/devtools/devtools_agent_host_impl.cc +++ b/content/browser/devtools/devtools_agent_host_impl.cc
@@ -117,7 +117,6 @@ } client_ = client; Attach(); - DevToolsManager::GetInstance()->AgentHostChanged(this); } void DevToolsAgentHostImpl::DetachClient() { @@ -127,7 +126,6 @@ scoped_refptr<DevToolsAgentHostImpl> protect(this); client_ = NULL; Detach(); - DevToolsManager::GetInstance()->AgentHostChanged(this); } bool DevToolsAgentHostImpl::IsAttached() { @@ -164,7 +162,6 @@ DevToolsAgentHostClient* client = client_; client_ = NULL; client->AgentHostClosed(this, false); - DevToolsManager::GetInstance()->AgentHostChanged(this); } void DevToolsAgentHostImpl::SendMessageToClient(const std::string& message) { @@ -190,7 +187,6 @@ agent_host->client_ = NULL; client->AgentHostClosed(agent_host, true); agent_host->Detach(); - DevToolsManager::GetInstance()->AgentHostChanged(protect); } } } @@ -219,6 +215,7 @@ DevToolsAgentHostImpl* agent_host, bool attached) { AgentStateCallbacks copy(g_callbacks.Get()); DevToolsManager* manager = DevToolsManager::GetInstance(); + manager->AgentHostStateChanged(agent_host, attached); if (manager->delegate()) manager->delegate()->DevToolsAgentStateChanged(agent_host, attached); for (AgentStateCallbacks::iterator it = copy.begin(); it != copy.end(); ++it)
diff --git a/content/browser/devtools/devtools_manager.cc b/content/browser/devtools/devtools_manager.cc index c30d3e9..1fa19d88 100644 --- a/content/browser/devtools/devtools_manager.cc +++ b/content/browser/devtools/devtools_manager.cc
@@ -10,16 +10,9 @@ #include "content/browser/devtools/devtools_netlog_observer.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/content_browser_client.h" -#include "content/public/browser/devtools_target.h" namespace content { -namespace { - -int kObserverThrottleInterval = 500; // ms - -} // namespace - // static DevToolsManager* DevToolsManager::GetInstance() { return Singleton<DevToolsManager>::get(); @@ -27,105 +20,32 @@ DevToolsManager::DevToolsManager() : delegate_(GetContentClient()->browser()->GetDevToolsManagerDelegate()), - update_target_list_required_(false), - update_target_list_scheduled_(false), - update_target_list_callback_(base::Bind( - &DevToolsManager::UpdateTargetListThrottled, - base::Unretained(this))) { + attached_hosts_count_(0) { } DevToolsManager::~DevToolsManager() { - DCHECK(!attached_hosts_.size()); - update_target_list_callback_.Cancel(); + DCHECK(!attached_hosts_count_); } -void DevToolsManager::RenderViewCreated( - WebContents* web_contents, RenderViewHost* rvh) { - if (observer_list_.might_have_observers()) { - // Force agent host creation. - DevToolsAgentHost::GetOrCreateFor(web_contents); - } -} - -void DevToolsManager::AgentHostChanged( - scoped_refptr<DevToolsAgentHost> agent_host) { - bool was_attached = - attached_hosts_.find(agent_host.get()) != attached_hosts_.end(); - if (agent_host->IsAttached() && !was_attached) { - if (!attached_hosts_.size()) { +void DevToolsManager::AgentHostStateChanged( + DevToolsAgentHostImpl* agent_host, bool attached) { + if (attached) { + if (!attached_hosts_count_) { BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, base::Bind(&DevToolsNetLogObserver::Attach)); } - attached_hosts_.insert(agent_host.get()); - } else if (!agent_host->IsAttached() && was_attached) { - attached_hosts_.erase(agent_host.get()); - if (!attached_hosts_.size()) { + ++attached_hosts_count_; + } else { + --attached_hosts_count_; + if (!attached_hosts_count_) { BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, base::Bind(&DevToolsNetLogObserver::Detach)); } } - - UpdateTargetList(); -} - -void DevToolsManager::AddObserver(Observer* observer) { - observer_list_.AddObserver(observer); - UpdateTargetList(); -} - -void DevToolsManager::RemoveObserver(Observer* observer) { - observer_list_.RemoveObserver(observer); -} - -void DevToolsManager::UpdateTargetList() { - if (!observer_list_.might_have_observers()) - return; - - update_target_list_required_ = true; - if (!update_target_list_scheduled_) - UpdateTargetListThrottled(); -} - -void DevToolsManager::UpdateTargetListThrottled() { - if (!update_target_list_required_) { - update_target_list_scheduled_ = false; - return; - } - - update_target_list_scheduled_ = true; - if (scheduler_.is_null()) { - base::MessageLoop::current()->PostDelayedTask( - FROM_HERE, - update_target_list_callback_.callback(), - base::TimeDelta::FromMilliseconds(kObserverThrottleInterval)); - } else { - scheduler_.Run(update_target_list_callback_.callback()); - } - - update_target_list_required_ = false; - if (!delegate_) { - Observer::TargetList empty_list; - NotifyTargetListChanged(empty_list); - return; - } - delegate_->EnumerateTargets(base::Bind( - &DevToolsManager::NotifyTargetListChanged, - base::Unretained(this))); -} - -void DevToolsManager::NotifyTargetListChanged( - const Observer::TargetList& targets) { - FOR_EACH_OBSERVER(Observer, observer_list_, TargetListChanged(targets)); - STLDeleteContainerPointers(targets.begin(), targets.end()); -} - -void DevToolsManager::SetUpForTest(Scheduler scheduler) { - scheduler_ = scheduler; - delegate_.reset(GetContentClient()->browser()->GetDevToolsManagerDelegate()); } } // namespace content
diff --git a/content/browser/devtools/devtools_manager.h b/content/browser/devtools/devtools_manager.h index caedf01f..3e29a7a 100644 --- a/content/browser/devtools/devtools_manager.h +++ b/content/browser/devtools/devtools_manager.h
@@ -5,40 +5,20 @@ #ifndef CONTENT_BROWSER_DEVTOOLS_DEVTOOLS_MANAGER_H_ #define CONTENT_BROWSER_DEVTOOLS_DEVTOOLS_MANAGER_H_ -#include <map> -#include <set> -#include <string> -#include <vector> - -#include "base/cancelable_callback.h" #include "base/compiler_specific.h" #include "base/memory/singleton.h" -#include "base/observer_list.h" #include "content/common/content_export.h" #include "content/public/browser/devtools_manager_delegate.h" namespace content { -class DevToolsAgentHost; -class RenderViewHost; -class WebContents; +class DevToolsAgentHostImpl; // This class is a singleton that manage global DevTools state for the whole // browser. +// TODO(dgozman): remove this class entirely. class CONTENT_EXPORT DevToolsManager { public: - class Observer { - public: - virtual ~Observer() {} - - typedef DevToolsManagerDelegate::TargetList TargetList; - - // Called when any target information changed. Targets in the list are - // owned by DevToolsManager, so they should not be accessed outside of - // this method. - virtual void TargetListChanged(const TargetList& targets) {} - }; - // Returns single instance of this class. The instance is destroyed on the // browser main loop exit so this method MUST NOT be called after that point. static DevToolsManager* GetInstance(); @@ -48,29 +28,13 @@ DevToolsManagerDelegate* delegate() const { return delegate_.get(); } - void AddObserver(Observer* observer); - void RemoveObserver(Observer* observer); - - void RenderViewCreated(WebContents* web_contents, RenderViewHost* rvh); - void AgentHostChanged(scoped_refptr<DevToolsAgentHost> agent_host); - - typedef base::Callback<void(base::Closure)> Scheduler; - void SetUpForTest(Scheduler scheduler); + void AgentHostStateChanged(DevToolsAgentHostImpl* agent_host, bool attached); private: friend struct DefaultSingletonTraits<DevToolsManager>; - void UpdateTargetList(); - void UpdateTargetListThrottled(); - void NotifyTargetListChanged(const Observer::TargetList& targets); - scoped_ptr<DevToolsManagerDelegate> delegate_; - ObserverList<Observer> observer_list_; - std::set<DevToolsAgentHost*> attached_hosts_; - bool update_target_list_required_; - bool update_target_list_scheduled_; - base::CancelableClosure update_target_list_callback_; - Scheduler scheduler_; + int attached_hosts_count_; DISALLOW_COPY_AND_ASSIGN(DevToolsManager); };
diff --git a/content/browser/devtools/devtools_manager_unittest.cc b/content/browser/devtools/devtools_manager_unittest.cc index ec5013bc..5231d5e1 100644 --- a/content/browser/devtools/devtools_manager_unittest.cc +++ b/content/browser/devtools/devtools_manager_unittest.cc
@@ -168,31 +168,6 @@ } }; -class TestDevToolsManagerObserver : public DevToolsManager::Observer { - public: - TestDevToolsManagerObserver() - : updates_count_(0) {} - ~TestDevToolsManagerObserver() override {} - - int updates_count() { return updates_count_; } - const std::vector<scoped_refptr<DevToolsAgentHost>> hosts() { - return hosts_; - } - - void TargetListChanged(const TargetList& targets) override { - updates_count_++; - hosts_.clear(); - for (TargetList::const_iterator it = targets.begin(); - it != targets.end(); ++it) { - hosts_.push_back((*it)->GetAgentHost()); - } - } - - private: - int updates_count_; - std::vector<scoped_refptr<DevToolsAgentHost>> hosts_; -}; - } // namespace class DevToolsManagerTest : public RenderViewHostImplTestHarness { @@ -205,12 +180,10 @@ RenderViewHostImplTestHarness::SetUp(); TestDevToolsClientHost::ResetCounters(); old_browser_client_ = SetBrowserClientForTesting(&browser_client_); - DevToolsManager::GetInstance()->SetUpForTest(DevToolsManager::Scheduler()); } void TearDown() override { SetBrowserClientForTesting(old_browser_client_); - DevToolsManager::GetInstance()->SetUpForTest(DevToolsManager::Scheduler()); RenderViewHostImplTestHarness::TearDown(); } @@ -357,109 +330,4 @@ client_host.Close(); } -class TestDevToolsManagerScheduler { - public: - DevToolsManager::Scheduler callback() { - return base::Bind(&TestDevToolsManagerScheduler::Schedule, - base::Unretained(this)); - } - - void Run() { - ASSERT_FALSE(closure_.is_null()); - base::Closure copy = closure_; - closure_.Reset(); - copy.Run(); - } - - bool IsEmpty() { - return closure_.is_null(); - } - - private: - void Schedule(base::Closure closure) { - EXPECT_TRUE(closure_.is_null()); - closure_ = closure; - } - - base::Closure closure_; -}; - -TEST_F(DevToolsManagerTest, TestObserver) { - GURL url1("data:text/html,<body>Body1</body>"); - GURL url2("data:text/html,<body>Body2</body>"); - GURL url3("data:text/html,<body>Body3</body>"); - - TestDevToolsManagerScheduler scheduler; - DevToolsManager* manager = DevToolsManager::GetInstance(); - manager->SetUpForTest(scheduler.callback()); - - contents()->NavigateAndCommit(url1); - RunAllPendingInMessageLoop(); - - scoped_ptr<TestDevToolsManagerObserver> observer( - new TestDevToolsManagerObserver()); - manager->AddObserver(observer.get()); - // Added observer should get an update. - EXPECT_EQ(1, observer->updates_count()); - ASSERT_EQ(1u, observer->hosts().size()); - EXPECT_EQ(contents(), observer->hosts()[0]->GetWebContents()); - EXPECT_EQ(url1.spec(), observer->hosts()[0]->GetURL().spec()); - - contents()->NavigateAndCommit(url2); - RunAllPendingInMessageLoop(); - contents()->NavigateAndCommit(url3); - scheduler.Run(); - // Updates should be coalesced. - EXPECT_EQ(2, observer->updates_count()); - ASSERT_EQ(1u, observer->hosts().size()); - EXPECT_EQ(contents(), observer->hosts()[0]->GetWebContents()); - EXPECT_EQ(url3.spec(), observer->hosts()[0]->GetURL().spec()); - - // Check there were no extra updates. - scheduler.Run(); - EXPECT_TRUE(scheduler.IsEmpty()); - EXPECT_EQ(2, observer->updates_count()); - - scoped_ptr<WorkerStoragePartition> partition(new WorkerStoragePartition( - browser_context()->GetRequestContext(), - NULL, NULL, NULL, NULL, NULL, NULL, NULL)); - WorkerStoragePartitionId partition_id(*partition.get()); - - GURL shared_worker_url("http://example.com/shared_worker.js"); - SharedWorkerInstance shared_worker( - shared_worker_url, - base::string16(), - base::string16(), - blink::WebContentSecurityPolicyTypeReport, - browser_context()->GetResourceContext(), - partition_id); - SharedWorkerDevToolsManager::GetInstance()->WorkerCreated( - 1, 1, shared_worker); - contents()->NavigateAndCommit(url2); - - EXPECT_EQ(3, observer->updates_count()); - ASSERT_EQ(2u, observer->hosts().size()); - EXPECT_EQ(contents(), observer->hosts()[0]->GetWebContents()); - EXPECT_EQ(url2.spec(), observer->hosts()[0]->GetURL().spec()); - EXPECT_EQ(DevToolsAgentHost::TYPE_SHARED_WORKER, - observer->hosts()[1]->GetType()); - EXPECT_EQ(shared_worker_url.spec(), observer->hosts()[1]->GetURL().spec()); - - SharedWorkerDevToolsManager::GetInstance()->WorkerDestroyed(1, 1); - scheduler.Run(); - EXPECT_EQ(4, observer->updates_count()); - ASSERT_EQ(1u, observer->hosts().size()); - EXPECT_EQ(contents(), observer->hosts()[0]->GetWebContents()); - EXPECT_EQ(url2.spec(), observer->hosts()[0]->GetURL().spec()); - - // Check there were no extra updates. - scheduler.Run(); - EXPECT_TRUE(scheduler.IsEmpty()); - EXPECT_EQ(4, observer->updates_count()); - - manager->RemoveObserver(observer.get()); - - EXPECT_TRUE(scheduler.IsEmpty()); -} - } // namespace content
diff --git a/content/browser/devtools/devtools_netlog_observer.cc b/content/browser/devtools/devtools_netlog_observer.cc index cb6b84f2d..f20ef34 100644 --- a/content/browser/devtools/devtools_netlog_observer.cc +++ b/content/browser/devtools/devtools_netlog_observer.cc
@@ -169,7 +169,8 @@ net::NetLog* net_log = GetContentClient()->browser()->GetNetLog(); if (net_log) { instance_ = new DevToolsNetLogObserver(); - net_log->DeprecatedAddObserver(instance_, net::NetLog::LOG_ALL_BUT_BYTES); + net_log->DeprecatedAddObserver( + instance_, net::NetLogCaptureMode::IncludeCookiesAndCredentials()); } }
diff --git a/content/browser/devtools/ipc_devtools_agent_host.cc b/content/browser/devtools/ipc_devtools_agent_host.cc index 4fa45eb..dc3eebe 100644 --- a/content/browser/devtools/ipc_devtools_agent_host.cc +++ b/content/browser/devtools/ipc_devtools_agent_host.cc
@@ -8,7 +8,7 @@ void IPCDevToolsAgentHost::Attach() { SendMessageToAgent(new DevToolsAgentMsg_Attach(MSG_ROUTING_NONE, GetId())); - OnClientAttached(); + OnClientAttached(false); } void IPCDevToolsAgentHost::Detach() { @@ -41,7 +41,7 @@ void IPCDevToolsAgentHost::Reattach() { SendMessageToAgent(new DevToolsAgentMsg_Reattach( MSG_ROUTING_NONE, GetId(), state_cookie_)); - OnClientAttached(); + OnClientAttached(true); } void IPCDevToolsAgentHost::ProcessChunkedMessageFromAgent(
diff --git a/content/browser/devtools/ipc_devtools_agent_host.h b/content/browser/devtools/ipc_devtools_agent_host.h index 754be5ee..1f910784 100644 --- a/content/browser/devtools/ipc_devtools_agent_host.h +++ b/content/browser/devtools/ipc_devtools_agent_host.h
@@ -30,7 +30,7 @@ void ProcessChunkedMessageFromAgent(const DevToolsMessageChunk& chunk); virtual void SendMessageToAgent(IPC::Message* msg) = 0; - virtual void OnClientAttached() = 0; + virtual void OnClientAttached(bool reattached) = 0; virtual void OnClientDetached() = 0; private:
diff --git a/content/browser/devtools/protocol/service_worker_handler.cc b/content/browser/devtools/protocol/service_worker_handler.cc index 939b993..6f426f4 100644 --- a/content/browser/devtools/protocol/service_worker_handler.cc +++ b/content/browser/devtools/protocol/service_worker_handler.cc
@@ -13,7 +13,6 @@ #include "content/browser/frame_host/frame_tree.h" #include "content/browser/frame_host/frame_tree_node.h" #include "content/browser/frame_host/render_frame_host_impl.h" -#include "content/browser/service_worker/service_worker_context_core.h" #include "content/browser/service_worker/service_worker_context_watcher.h" #include "content/browser/service_worker/service_worker_context_wrapper.h" #include "content/browser/service_worker/service_worker_version.h" @@ -151,11 +150,8 @@ void StopServiceWorkerOnIO(scoped_refptr<ServiceWorkerContextWrapper> context, int64 version_id) { - ServiceWorkerContextCore* context_core = context->context(); - if (!context_core) - return; if (content::ServiceWorkerVersion* version = - context_core->GetLiveVersion(version_id)) { + context->GetLiveVersion(version_id)) { version->StopWorker(base::Bind(&StatusNoOp)); } } @@ -164,11 +160,8 @@ scoped_refptr<ServiceWorkerContextWrapper> context, int64 version_id, const base::Callback<void(int, int)>& callback) { - ServiceWorkerContextCore* context_core = context->context(); - if (!context_core) - return; if (content::ServiceWorkerVersion* version = - context_core->GetLiveVersion(version_id)) { + context->GetLiveVersion(version_id)) { BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, base::Bind( @@ -458,7 +451,8 @@ void ServiceWorkerHandler::WorkerCreated( ServiceWorkerDevToolsAgentHost* host) { auto hosts = GetMatchingServiceWorkers(urls_); - if (hosts.find(host->GetId()) != hosts.end() && !host->IsAttached()) + if (hosts.find(host->GetId()) != hosts.end() && !host->IsAttached() && + !host->IsPausedForDebugOnStart()) host->PauseForDebugOnStart(); }
diff --git a/content/browser/devtools/render_frame_devtools_agent_host.cc b/content/browser/devtools/render_frame_devtools_agent_host.cc index 9fe66ae..9d09b98 100644 --- a/content/browser/devtools/render_frame_devtools_agent_host.cc +++ b/content/browser/devtools/render_frame_devtools_agent_host.cc
@@ -9,7 +9,6 @@ #include "base/strings/utf_string_conversions.h" #include "content/browser/child_process_security_policy_impl.h" #include "content/browser/devtools/devtools_frame_trace_recorder.h" -#include "content/browser/devtools/devtools_manager.h" #include "content/browser/devtools/protocol/devtools_protocol_handler.h" #include "content/browser/devtools/protocol/dom_handler.h" #include "content/browser/devtools/protocol/emulation_handler.h" @@ -129,8 +128,7 @@ RenderFrameDevToolsAgentHost* agent_host = FindAgentHost(pending); if (!agent_host) return; - agent_host->DisconnectRenderFrameHost(); - agent_host->ConnectRenderFrameHost(current); + agent_host->ReattachToRenderFrameHost(current); } RenderFrameDevToolsAgentHost::RenderFrameDevToolsAgentHost(RenderFrameHost* rfh) @@ -168,7 +166,6 @@ SetRenderFrameHost(rfh); g_instances.Get().push_back(this); AddRef(); // Balanced in RenderFrameHostDestroyed. - DevToolsManager::GetInstance()->AgentHostChanged(this); } BrowserContext* RenderFrameDevToolsAgentHost::GetBrowserContext() { @@ -187,7 +184,7 @@ render_frame_host_->Send(msg); } -void RenderFrameDevToolsAgentHost::OnClientAttached() { +void RenderFrameDevToolsAgentHost::OnClientAttached(bool reattached) { if (!render_frame_host_) return; @@ -323,9 +320,10 @@ void RenderFrameDevToolsAgentHost::DestroyOnRenderFrameGone() { DCHECK(render_frame_host_); scoped_refptr<RenderFrameDevToolsAgentHost> protect(this); + if (IsAttached()) + OnClientDetached(); HostClosed(); ClearRenderFrameHost(); - DevToolsManager::GetInstance()->AgentHostChanged(this); Release(); } @@ -390,16 +388,6 @@ page_handler_->DidDetachInterstitialPage(); } -void RenderFrameDevToolsAgentHost::TitleWasSet( - NavigationEntry* entry, bool explicit_set) { - DevToolsManager::GetInstance()->AgentHostChanged(this); -} - -void RenderFrameDevToolsAgentHost::NavigationEntryCommitted( - const LoadCommittedDetails& load_details) { - DevToolsManager::GetInstance()->AgentHostChanged(this); -} - void RenderFrameDevToolsAgentHost::DidCommitProvisionalLoadForFrame( RenderFrameHost* render_frame_host, const GURL& url,
diff --git a/content/browser/devtools/render_frame_devtools_agent_host.h b/content/browser/devtools/render_frame_devtools_agent_host.h index 1808211..a77cdbe 100644 --- a/content/browser/devtools/render_frame_devtools_agent_host.h +++ b/content/browser/devtools/render_frame_devtools_agent_host.h
@@ -78,7 +78,7 @@ // IPCDevToolsAgentHost overrides. void SendMessageToAgent(IPC::Message* msg) override; - void OnClientAttached() override; + void OnClientAttached(bool reattached) override; void OnClientDetached() override; // WebContentsObserver overrides. @@ -93,9 +93,6 @@ bool OnMessageReceived(const IPC::Message& message) override; void DidAttachInterstitialPage() override; void DidDetachInterstitialPage() override; - void TitleWasSet(NavigationEntry* entry, bool explicit_set) override; - void NavigationEntryCommitted( - const LoadCommittedDetails& load_details) override; void DidCommitProvisionalLoadForFrame( RenderFrameHost* render_frame_host, const GURL& url,
diff --git a/content/browser/devtools/service_worker_devtools_agent_host.cc b/content/browser/devtools/service_worker_devtools_agent_host.cc index a8cc6a2..5a36ba1 100644 --- a/content/browser/devtools/service_worker_devtools_agent_host.cc +++ b/content/browser/devtools/service_worker_devtools_agent_host.cc
@@ -92,8 +92,8 @@ service_worker_->version_id())); } -void ServiceWorkerDevToolsAgentHost::OnClientAttached() { - WorkerDevToolsAgentHost::OnClientAttached(); +void ServiceWorkerDevToolsAgentHost::OnClientAttached(bool reattached) { + WorkerDevToolsAgentHost::OnClientAttached(reattached); BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, base::Bind(&SetDevToolsAttachedOnIO, service_worker_->context_weak(),
diff --git a/content/browser/devtools/service_worker_devtools_agent_host.h b/content/browser/devtools/service_worker_devtools_agent_host.h index 4e6ce3a..b0535cc 100644 --- a/content/browser/devtools/service_worker_devtools_agent_host.h +++ b/content/browser/devtools/service_worker_devtools_agent_host.h
@@ -33,7 +33,7 @@ bool Close() override; // IPCDevToolsAgentHost override. - void OnClientAttached() override; + void OnClientAttached(bool reattached) override; void OnClientDetached() override; bool Matches(const ServiceWorkerIdentifier& other);
diff --git a/content/browser/devtools/service_worker_devtools_manager.cc b/content/browser/devtools/service_worker_devtools_manager.cc index a423066..f6eb93b 100644 --- a/content/browser/devtools/service_worker_devtools_manager.cc +++ b/content/browser/devtools/service_worker_devtools_manager.cc
@@ -4,7 +4,6 @@ #include "content/browser/devtools/service_worker_devtools_manager.h" -#include "content/browser/devtools/devtools_manager.h" #include "content/browser/devtools/ipc_devtools_agent_host.h" #include "content/browser/devtools/service_worker_devtools_agent_host.h" #include "content/public/browser/browser_thread.h" @@ -78,7 +77,6 @@ id, service_worker_id); workers_[id] = host.get(); FOR_EACH_OBSERVER(Observer, observer_list_, WorkerCreated(host.get())); - DevToolsManager::GetInstance()->AgentHostChanged(host.get()); if (debug_service_worker_on_start_) host->PauseForDebugOnStart(); return host->IsPausedForDebugOnStart(); @@ -89,7 +87,6 @@ agent_host->WorkerRestarted(id); workers_.erase(it); workers_[id] = agent_host; - DevToolsManager::GetInstance()->AgentHostChanged(agent_host); return agent_host->IsAttached(); } @@ -128,7 +125,6 @@ DCHECK(it != workers_.end()); scoped_refptr<WorkerDevToolsAgentHost> agent_host(it->second); agent_host->WorkerDestroyed(); - DevToolsManager::GetInstance()->AgentHostChanged(agent_host); FOR_EACH_OBSERVER(Observer, observer_list_, WorkerDestroyed(it->second)); }
diff --git a/content/browser/devtools/shared_worker_devtools_manager.cc b/content/browser/devtools/shared_worker_devtools_manager.cc index 3fb2cf1..3490e80 100644 --- a/content/browser/devtools/shared_worker_devtools_manager.cc +++ b/content/browser/devtools/shared_worker_devtools_manager.cc
@@ -4,7 +4,6 @@ #include "content/browser/devtools/shared_worker_devtools_manager.h" -#include "content/browser/devtools/devtools_manager.h" #include "content/browser/devtools/shared_worker_devtools_agent_host.h" #include "content/browser/shared_worker/shared_worker_instance.h" #include "content/public/browser/browser_thread.h" @@ -44,7 +43,6 @@ FindExistingWorkerAgentHost(instance); if (it == workers_.end()) { workers_[id] = new SharedWorkerDevToolsAgentHost(id, instance); - DevToolsManager::GetInstance()->AgentHostChanged(workers_[id]); return false; } @@ -53,7 +51,6 @@ agent_host->WorkerRestarted(id); workers_.erase(it); workers_[id] = agent_host; - DevToolsManager::GetInstance()->AgentHostChanged(agent_host); return true; } @@ -76,7 +73,6 @@ DCHECK(it != workers_.end()); scoped_refptr<SharedWorkerDevToolsAgentHost> agent_host(it->second); agent_host->WorkerDestroyed(); - DevToolsManager::GetInstance()->AgentHostChanged(agent_host); } void SharedWorkerDevToolsManager::RemoveInspectedWorkerData(WorkerId id) {
diff --git a/content/browser/devtools/worker_devtools_agent_host.cc b/content/browser/devtools/worker_devtools_agent_host.cc index fcf8dd7..a0c1fc6 100644 --- a/content/browser/devtools/worker_devtools_agent_host.cc +++ b/content/browser/devtools/worker_devtools_agent_host.cc
@@ -35,8 +35,9 @@ IPCDevToolsAgentHost::Attach(); } -void WorkerDevToolsAgentHost::OnClientAttached() { - DevToolsAgentHostImpl::NotifyCallbacks(this, true); +void WorkerDevToolsAgentHost::OnClientAttached(bool reattached) { + if (!reattached) + DevToolsAgentHostImpl::NotifyCallbacks(this, true); } void WorkerDevToolsAgentHost::OnClientDetached() {
diff --git a/content/browser/devtools/worker_devtools_agent_host.h b/content/browser/devtools/worker_devtools_agent_host.h index 7bd5128..30fee5a 100644 --- a/content/browser/devtools/worker_devtools_agent_host.h +++ b/content/browser/devtools/worker_devtools_agent_host.h
@@ -24,7 +24,7 @@ // IPCDevToolsAgentHost implementation. void SendMessageToAgent(IPC::Message* message) override; void Attach() override; - void OnClientAttached() override; + void OnClientAttached(bool reattached) override; void OnClientDetached() override; // IPC::Listener implementation.
diff --git a/content/browser/dom_storage/dom_storage_area.cc b/content/browser/dom_storage/dom_storage_area.cc index 9ff7bfb..56a1fd00 100644 --- a/content/browser/dom_storage/dom_storage_area.cc +++ b/content/browser/dom_storage/dom_storage_area.cc
@@ -20,6 +20,7 @@ #include "content/browser/dom_storage/session_storage_database_adapter.h" #include "content/common/dom_storage/dom_storage_map.h" #include "content/common/dom_storage/dom_storage_types.h" +#include "content/public/browser/browser_thread.h" #include "storage/browser/database/database_util.h" #include "storage/common/database/database_identifier.h" #include "storage/common/fileapi/file_system_util.h" @@ -364,19 +365,28 @@ DCHECK(!is_shutdown_); if (!commit_batch_) { commit_batch_.reset(new CommitBatch()); - - // Start a timer to commit any changes that accrue in the batch, but only if - // no commits are currently in flight. In that case the timer will be - // started after the commits have happened. - if (!commit_batches_in_flight_) { - task_runner_->PostDelayedTask( - FROM_HERE, base::Bind(&DOMStorageArea::OnCommitTimer, this), - ComputeCommitDelay()); - } + BrowserThread::PostAfterStartupTask( + FROM_HERE, task_runner_, + base::Bind(&DOMStorageArea::StartCommitTimer, this)); } return commit_batch_.get(); } +void DOMStorageArea::StartCommitTimer() { + if (is_shutdown_ || !commit_batch_) + return; + + // Start a timer to commit any changes that accrue in the batch, but only if + // no commits are currently in flight. In that case the timer will be + // started after the commits have happened. + if (commit_batches_in_flight_) + return; + + task_runner_->PostDelayedTask( + FROM_HERE, base::Bind(&DOMStorageArea::OnCommitTimer, this), + ComputeCommitDelay()); +} + base::TimeDelta DOMStorageArea::ComputeCommitDelay() const { base::TimeDelta elapsed_time = base::TimeTicks::Now() - start_time_; base::TimeDelta delay = std::max(
diff --git a/content/browser/dom_storage/dom_storage_area.h b/content/browser/dom_storage/dom_storage_area.h index d094789..3a4b166 100644 --- a/content/browser/dom_storage/dom_storage_area.h +++ b/content/browser/dom_storage/dom_storage_area.h
@@ -144,6 +144,7 @@ // disk on the commit sequence, and to call back on the primary // task sequence when complete. CommitBatch* CreateCommitBatchIfNeeded(); + void StartCommitTimer(); void OnCommitTimer(); void PostCommitTask(); void CommitChanges(const CommitBatch* commit_batch);
diff --git a/content/browser/dom_storage/dom_storage_area_unittest.cc b/content/browser/dom_storage/dom_storage_area_unittest.cc index 11155909..aa5653da 100644 --- a/content/browser/dom_storage/dom_storage_area_unittest.cc +++ b/content/browser/dom_storage/dom_storage_area_unittest.cc
@@ -6,8 +6,8 @@ #include "base/files/file_util.h" #include "base/files/scoped_temp_dir.h" #include "base/message_loop/message_loop.h" -#include "base/message_loop/message_loop_proxy.h" #include "base/strings/utf_string_conversions.h" +#include "base/thread_task_runner_handle.h" #include "base/threading/sequenced_worker_pool.h" #include "base/time/time.h" #include "content/browser/dom_storage/dom_storage_area.h" @@ -16,6 +16,7 @@ #include "content/browser/dom_storage/dom_storage_task_runner.h" #include "content/browser/dom_storage/local_storage_database_adapter.h" #include "content/common/dom_storage/dom_storage_types.h" +#include "content/public/browser/browser_thread.h" #include "testing/gtest/include/gtest/gtest.h" using base::ASCIIToUTF16; @@ -39,7 +40,20 @@ const base::string16 kValue2; // Method used in the CommitTasks test case. - void InjectedCommitSequencingTask(DOMStorageArea* area) { + void InjectedCommitSequencingTask1( + const scoped_refptr<DOMStorageArea>& area) { + // At this point the StartCommitTimer task has run and + // the OnCommitTimer task is queued. We want to inject after + // that. + base::MessageLoop::current()->PostTask( + FROM_HERE, + base::Bind(&DOMStorageAreaTest::InjectedCommitSequencingTask2, + base::Unretained(this), + area)); + } + + void InjectedCommitSequencingTask2( + const scoped_refptr<DOMStorageArea>& area) { // At this point the OnCommitTimer has run. // Verify that it put a commit in flight. EXPECT_EQ(1, area->commit_batches_in_flight_); @@ -261,13 +275,14 @@ // those will also get committed. EXPECT_TRUE(area->SetItem(kKey, kValue, &old_value)); EXPECT_TRUE(area->HasUncommittedChanges()); - // At this point the OnCommitTimer task has been posted. We inject - // another task in the queue that will execute after the timer task, - // but before the CommitChanges task. From within our injected task, - // we'll make an additional SetItem() call. - base::MessageLoop::current()->PostTask( - FROM_HERE, - base::Bind(&DOMStorageAreaTest::InjectedCommitSequencingTask, + // At this point the StartCommitTimer task has been posted to the after + // startup task queue. We inject another task in the queue that will + // execute when the CommitChanges task is inflight. From within our + // injected task, we'll make an additional SetItem() call and verify + // that a new commit batch is created for that additional change. + BrowserThread::PostAfterStartupTask( + FROM_HERE, base::ThreadTaskRunnerHandle::Get(), + base::Bind(&DOMStorageAreaTest::InjectedCommitSequencingTask1, base::Unretained(this), area)); base::MessageLoop::current()->RunUntilIdle();
diff --git a/content/browser/download/download_file_impl.cc b/content/browser/download/download_file_impl.cc index 203c689..4c8203d 100644 --- a/content/browser/download/download_file_impl.cc +++ b/content/browser/download/download_file_impl.cc
@@ -337,7 +337,7 @@ &DownloadDestinationObserver::DestinationCompleted, observer_, hash)); } - if (bound_net_log_.IsLogging()) { + if (bound_net_log_.GetCaptureMode().enabled()) { bound_net_log_.AddEvent( net::NetLog::TYPE_DOWNLOAD_STREAM_DRAINED, base::Bind(&FileStreamDrainedNetLogCallback, total_incoming_data_size,
diff --git a/content/browser/download/download_item_impl.cc b/content/browser/download/download_item_impl.cc index a0dea56..dbcf7c4 100644 --- a/content/browser/download/download_item_impl.cc +++ b/content/browser/download/download_item_impl.cc
@@ -1055,7 +1055,7 @@ if (received_bytes_ > total_bytes_) total_bytes_ = 0; - if (bound_net_log_.IsLogging()) { + if (bound_net_log_.GetCaptureMode().enabled()) { bound_net_log_.AddEvent( net::NetLog::TYPE_DOWNLOAD_ITEM_UPDATED, net::NetLog::Int64Callback("bytes_so_far", received_bytes_)); @@ -1106,8 +1106,8 @@ file_name = GetURL().ExtractFileName(); } - base::Callback<base::Value*(net::NetLog::LogLevel)> active_data = base::Bind( - &ItemActivatedNetLogCallback, this, download_type, &file_name); + net::NetLog::ParametersCallback active_data = + base::Bind(&ItemActivatedNetLogCallback, this, download_type, &file_name); if (active) { bound_net_log_.BeginEvent( net::NetLog::TYPE_DOWNLOAD_ITEM_ACTIVE, active_data);
diff --git a/content/browser/download/download_net_log_parameters.cc b/content/browser/download/download_net_log_parameters.cc index 8fe755c..8d04ae1 100644 --- a/content/browser/download/download_net_log_parameters.cc +++ b/content/browser/download/download_net_log_parameters.cc
@@ -41,11 +41,10 @@ } // namespace -base::Value* ItemActivatedNetLogCallback( - const DownloadItem* download_item, - DownloadType download_type, - const std::string* file_name, - net::NetLog::LogLevel log_level) { +base::Value* ItemActivatedNetLogCallback(const DownloadItem* download_item, + DownloadType download_type, + const std::string* file_name, + net::NetLogCaptureMode capture_mode) { base::DictionaryValue* dict = new base::DictionaryValue(); dict->SetString("type", download_type_names[download_type]); @@ -62,9 +61,8 @@ return dict; } -base::Value* ItemCheckedNetLogCallback( - DownloadDangerType danger_type, - net::NetLog::LogLevel log_level) { +base::Value* ItemCheckedNetLogCallback(DownloadDangerType danger_type, + net::NetLogCaptureMode capture_mode) { base::DictionaryValue* dict = new base::DictionaryValue(); dict->SetString("danger_type", download_danger_names[danger_type]); @@ -74,7 +72,7 @@ base::Value* ItemRenamedNetLogCallback(const base::FilePath* old_filename, const base::FilePath* new_filename, - net::NetLog::LogLevel log_level) { + net::NetLogCaptureMode capture_mode) { base::DictionaryValue* dict = new base::DictionaryValue(); dict->SetString("old_filename", old_filename->AsUTF8Unsafe()); @@ -83,10 +81,11 @@ return dict; } -base::Value* ItemInterruptedNetLogCallback(DownloadInterruptReason reason, - int64 bytes_so_far, - const std::string* hash_state, - net::NetLog::LogLevel log_level) { +base::Value* ItemInterruptedNetLogCallback( + DownloadInterruptReason reason, + int64 bytes_so_far, + const std::string* hash_state, + net::NetLogCaptureMode capture_mode) { base::DictionaryValue* dict = new base::DictionaryValue(); dict->SetString("interrupt_reason", DownloadInterruptReasonToString(reason)); @@ -101,7 +100,7 @@ DownloadInterruptReason reason, int64 bytes_so_far, const std::string* hash_state, - net::NetLog::LogLevel log_level) { + net::NetLogCaptureMode capture_mode) { base::DictionaryValue* dict = new base::DictionaryValue(); dict->SetString("user_initiated", user_initiated ? "true" : "false"); @@ -115,7 +114,7 @@ base::Value* ItemCompletingNetLogCallback(int64 bytes_so_far, const std::string* final_hash, - net::NetLog::LogLevel log_level) { + net::NetLogCaptureMode capture_mode) { base::DictionaryValue* dict = new base::DictionaryValue(); dict->SetString("bytes_so_far", base::Int64ToString(bytes_so_far)); @@ -126,7 +125,7 @@ } base::Value* ItemFinishedNetLogCallback(bool auto_opened, - net::NetLog::LogLevel log_level) { + net::NetLogCaptureMode capture_mode) { base::DictionaryValue* dict = new base::DictionaryValue(); dict->SetString("auto_opened", auto_opened ? "yes" : "no"); @@ -136,7 +135,7 @@ base::Value* ItemCanceledNetLogCallback(int64 bytes_so_far, const std::string* hash_state, - net::NetLog::LogLevel log_level) { + net::NetLogCaptureMode capture_mode) { base::DictionaryValue* dict = new base::DictionaryValue(); dict->SetString("bytes_so_far", base::Int64ToString(bytes_so_far)); @@ -148,7 +147,7 @@ base::Value* FileOpenedNetLogCallback(const base::FilePath* file_name, int64 start_offset, - net::NetLog::LogLevel log_level) { + net::NetLogCaptureMode capture_mode) { base::DictionaryValue* dict = new base::DictionaryValue(); dict->SetString("file_name", file_name->AsUTF8Unsafe()); @@ -157,9 +156,10 @@ return dict; } -base::Value* FileStreamDrainedNetLogCallback(size_t stream_size, - size_t num_buffers, - net::NetLog::LogLevel log_level) { +base::Value* FileStreamDrainedNetLogCallback( + size_t stream_size, + size_t num_buffers, + net::NetLogCaptureMode capture_mode) { base::DictionaryValue* dict = new base::DictionaryValue(); dict->SetInteger("stream_size", static_cast<int>(stream_size)); @@ -170,7 +170,7 @@ base::Value* FileRenamedNetLogCallback(const base::FilePath* old_filename, const base::FilePath* new_filename, - net::NetLog::LogLevel log_level) { + net::NetLogCaptureMode capture_mode) { base::DictionaryValue* dict = new base::DictionaryValue(); dict->SetString("old_filename", old_filename->AsUTF8Unsafe()); @@ -181,7 +181,7 @@ base::Value* FileErrorNetLogCallback(const char* operation, net::Error net_error, - net::NetLog::LogLevel log_level) { + net::NetLogCaptureMode capture_mode) { base::DictionaryValue* dict = new base::DictionaryValue(); dict->SetString("operation", operation); @@ -190,10 +190,11 @@ return dict; } -base::Value* FileInterruptedNetLogCallback(const char* operation, - int os_error, - DownloadInterruptReason reason, - net::NetLog::LogLevel log_level) { +base::Value* FileInterruptedNetLogCallback( + const char* operation, + int os_error, + DownloadInterruptReason reason, + net::NetLogCaptureMode capture_mode) { base::DictionaryValue* dict = new base::DictionaryValue(); dict->SetString("operation", operation);
diff --git a/content/browser/download/download_net_log_parameters.h b/content/browser/download/download_net_log_parameters.h index 6d027cf..de0c957 100644 --- a/content/browser/download/download_net_log_parameters.h +++ b/content/browser/download/download_net_log_parameters.h
@@ -26,74 +26,73 @@ }; // Returns NetLog parameters when a DownloadItem is activated. -base::Value* ItemActivatedNetLogCallback( - const DownloadItem* download_item, - DownloadType download_type, - const std::string* file_name, - net::NetLog::LogLevel log_level); +base::Value* ItemActivatedNetLogCallback(const DownloadItem* download_item, + DownloadType download_type, + const std::string* file_name, + net::NetLogCaptureMode capture_mode); // Returns NetLog parameters when a DownloadItem is checked for danger. -base::Value* ItemCheckedNetLogCallback( - DownloadDangerType danger_type, - net::NetLog::LogLevel log_level); +base::Value* ItemCheckedNetLogCallback(DownloadDangerType danger_type, + net::NetLogCaptureMode capture_mode); // Returns NetLog parameters when a DownloadItem is renamed. base::Value* ItemRenamedNetLogCallback(const base::FilePath* old_filename, const base::FilePath* new_filename, - net::NetLog::LogLevel log_level); + net::NetLogCaptureMode capture_mode); // Returns NetLog parameters when a DownloadItem is interrupted. base::Value* ItemInterruptedNetLogCallback(DownloadInterruptReason reason, int64 bytes_so_far, const std::string* hash_state, - net::NetLog::LogLevel log_level); + net::NetLogCaptureMode capture_mode); // Returns NetLog parameters when a DownloadItem is resumed. base::Value* ItemResumingNetLogCallback(bool user_initiated, DownloadInterruptReason reason, int64 bytes_so_far, const std::string* hash_state, - net::NetLog::LogLevel log_level); + net::NetLogCaptureMode capture_mode); // Returns NetLog parameters when a DownloadItem is completing. base::Value* ItemCompletingNetLogCallback(int64 bytes_so_far, const std::string* final_hash, - net::NetLog::LogLevel log_level); + net::NetLogCaptureMode capture_mode); // Returns NetLog parameters when a DownloadItem is finished. base::Value* ItemFinishedNetLogCallback(bool auto_opened, - net::NetLog::LogLevel log_level); + net::NetLogCaptureMode capture_mode); // Returns NetLog parameters when a DownloadItem is canceled. base::Value* ItemCanceledNetLogCallback(int64 bytes_so_far, const std::string* hash_state, - net::NetLog::LogLevel log_level); + net::NetLogCaptureMode capture_mode); // Returns NetLog parameters when a DownloadFile is opened. base::Value* FileOpenedNetLogCallback(const base::FilePath* file_name, int64 start_offset, - net::NetLog::LogLevel log_level); + net::NetLogCaptureMode capture_mode); // Returns NetLog parameters when a DownloadFile is opened. -base::Value* FileStreamDrainedNetLogCallback(size_t stream_size, - size_t num_buffers, - net::NetLog::LogLevel log_level); +base::Value* FileStreamDrainedNetLogCallback( + size_t stream_size, + size_t num_buffers, + net::NetLogCaptureMode capture_mode); // Returns NetLog parameters when a DownloadFile is renamed. base::Value* FileRenamedNetLogCallback(const base::FilePath* old_filename, const base::FilePath* new_filename, - net::NetLog::LogLevel log_level); + net::NetLogCaptureMode capture_mode); // Returns NetLog parameters when a File has an error. base::Value* FileErrorNetLogCallback(const char* operation, net::Error net_error, - net::NetLog::LogLevel log_level); + net::NetLogCaptureMode capture_mode); // Returns NetLog parameters for a download interruption. base::Value* FileInterruptedNetLogCallback(const char* operation, int os_error, DownloadInterruptReason reason, - net::NetLog::LogLevel log_level); + net::NetLogCaptureMode capture_mode); } // namespace content
diff --git a/content/browser/frame_host/navigator.h b/content/browser/frame_host/navigator.h index 8771dd2..3a27601e 100644 --- a/content/browser/frame_host/navigator.h +++ b/content/browser/frame_host/navigator.h
@@ -101,6 +101,7 @@ const GURL& url, SiteInstance* source_site_instance, const Referrer& referrer, + ui::PageTransition page_transition, WindowOpenDisposition disposition, bool should_replace_current_entry, bool user_gesture) {}
diff --git a/content/browser/frame_host/navigator_impl.cc b/content/browser/frame_host/navigator_impl.cc index 0bbd2bd..a00f9723d 100644 --- a/content/browser/frame_host/navigator_impl.cc +++ b/content/browser/frame_host/navigator_impl.cc
@@ -38,6 +38,7 @@ #include "content/public/common/resource_response.h" #include "content/public/common/url_constants.h" #include "content/public/common/url_utils.h" +#include "net/base/net_errors.h" namespace content { @@ -517,6 +518,7 @@ const GURL& url, SiteInstance* source_site_instance, const Referrer& referrer, + ui::PageTransition page_transition, WindowOpenDisposition disposition, bool should_replace_current_entry, bool user_gesture) { @@ -538,9 +540,9 @@ // redirects. http://crbug.com/311721. std::vector<GURL> redirect_chain; RequestTransferURL(render_frame_host, url, source_site_instance, - redirect_chain, referrer, ui::PAGE_TRANSITION_LINK, - disposition, GlobalRequestID(), - should_replace_current_entry, user_gesture); + redirect_chain, referrer, page_transition, disposition, + GlobalRequestID(), should_replace_current_entry, + user_gesture); } void NavigatorImpl::RequestTransferURL( @@ -729,6 +731,12 @@ NavigationRequest* navigation_request = frame_tree_node->navigation_request(); DCHECK(navigation_request); + // If the request was canceled by the user do not show an error page. + if (error_code == net::ERR_ABORTED) { + frame_tree_node->ResetNavigationRequest(false); + return; + } + // Select an appropriate renderer to show the error page. RenderFrameHostImpl* render_frame_host = frame_tree_node->render_manager()->GetFrameHostForNavigation(
diff --git a/content/browser/frame_host/navigator_impl.h b/content/browser/frame_host/navigator_impl.h index 7dd2a02..95ad6a7 100644 --- a/content/browser/frame_host/navigator_impl.h +++ b/content/browser/frame_host/navigator_impl.h
@@ -57,6 +57,7 @@ const GURL& url, SiteInstance* source_site_instance, const Referrer& referrer, + ui::PageTransition page_transition, WindowOpenDisposition disposition, bool should_replace_current_entry, bool user_gesture) override;
diff --git a/content/browser/frame_host/render_frame_host_impl.cc b/content/browser/frame_host/render_frame_host_impl.cc index fae00d89..57ba8e25 100644 --- a/content/browser/frame_host/render_frame_host_impl.cc +++ b/content/browser/frame_host/render_frame_host_impl.cc
@@ -67,6 +67,10 @@ #include "ui/accessibility/ax_tree_update.h" #include "url/gurl.h" +#if defined(OS_ANDROID) +#include "content/browser/mojo/service_registrar_android.h" +#endif + #if defined(OS_MACOSX) #include "content/browser/frame_host/popup_menu_helper_mac.h" #endif @@ -1668,8 +1672,14 @@ TRACE_EVENT1("navigation", "RenderFrameHostImpl::OpenURL", "url", validated_url.possibly_invalid_spec()); + ui::PageTransition transition = ui::PAGE_TRANSITION_LINK; + if (frame_tree_node_->parent()) { + transition = params.should_replace_current_entry + ? ui::PAGE_TRANSITION_AUTO_SUBFRAME + : ui::PAGE_TRANSITION_MANUAL_SUBFRAME; + } frame_tree_node_->navigator()->RequestOpenURL( - this, validated_url, source_site_instance, params.referrer, + this, validated_url, source_site_instance, params.referrer, transition, params.disposition, params.should_replace_current_entry, params.user_gesture); } @@ -1830,6 +1840,8 @@ #if defined(OS_ANDROID) service_registry_android_.reset( new ServiceRegistryAndroid(service_registry_.get())); + ServiceRegistrarAndroid::RegisterFrameHostServices( + service_registry_android_.get()); #endif }
diff --git a/content/browser/frame_host/render_frame_host_manager.cc b/content/browser/frame_host/render_frame_host_manager.cc index 865543a..b134423 100644 --- a/content/browser/frame_host/render_frame_host_manager.cc +++ b/content/browser/frame_host/render_frame_host_manager.cc
@@ -352,6 +352,13 @@ CancelPending(); } + // PlzNavigate: clean up the speculative RenderFrameHost if there is one. + if (base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kEnableBrowserSideNavigation) && + speculative_render_frame_host_) { + CleanUpNavigation(); + } + // This is not a cross-process navigation; the tab is being closed. render_frame_host_->render_view_host()->ClosePage(); } @@ -819,6 +826,17 @@ MSG_ROUTING_NONE, frame_tree_node_->IsMainFrame())) { return nullptr; } + + if (navigation_rfh == render_frame_host_) { + // TODO(nasko): This is a very ugly hack. The Chrome extensions process + // manager still uses NotificationService and expects to see a + // RenderViewHost changed notification after WebContents and + // RenderFrameHostManager are completely initialized. This should be + // removed once the process manager moves away from NotificationService. + // See https://crbug.com/462682. + delegate_->NotifyMainFrameSwappedFromRenderManager( + nullptr, render_frame_host_->render_view_host()); + } } return navigation_rfh;
diff --git a/content/browser/frame_host/render_frame_host_manager_unittest.cc b/content/browser/frame_host/render_frame_host_manager_unittest.cc index cfadba5..c5ea0e6 100644 --- a/content/browser/frame_host/render_frame_host_manager_unittest.cc +++ b/content/browser/frame_host/render_frame_host_manager_unittest.cc
@@ -489,7 +489,6 @@ TestRenderFrameHost* ntp_rfh = contents()->GetMainFrame(); // Send an update favicon message and make sure it works. - const base::string16 ntp_title = base::ASCIIToUTF16("NTP Title"); { PluginFaviconMessageObserver observer(contents()); EXPECT_TRUE(ntp_rfh->GetRenderViewHost()->OnMessageReceived( @@ -502,7 +501,6 @@ // site. ntp_rfh->GetSiteInstance()->increment_active_frame_count(); - // Navigate to a cross-site URL. NavigateActiveAndCommit(kDestUrl); TestRenderFrameHost* dest_rfh = contents()->GetMainFrame(); @@ -510,7 +508,6 @@ EXPECT_NE(ntp_rfh, dest_rfh); // The new RVH should be able to update its favicon. - const base::string16 dest_title = base::ASCIIToUTF16("Google"); { PluginFaviconMessageObserver observer(contents()); EXPECT_TRUE( @@ -571,6 +568,61 @@ EXPECT_TRUE(ntp_process_host->sink().GetUniqueMessageMatching(IPC_REPLY_ID)); } +// Test that the ViewHostMsg_UpdateFaviconURL IPC message is ignored if the +// renderer is in the STATE_PENDING_SWAP_OUT_STATE. The favicon code assumes +// that it only gets ViewHostMsg_UpdateFaviconURL messages for the most recently +// committed navigation for each WebContentsImpl. +TEST_F(RenderFrameHostManagerTest, UpdateFaviconURLWhilePendingSwapOut) { + const GURL kChromeURL("chrome://foo"); + const GURL kDestUrl("http://www.google.com/"); + std::vector<FaviconURL> icons; + + // Navigate our first tab to a chrome url and then to the destination. + NavigateActiveAndCommit(kChromeURL); + TestRenderFrameHost* rfh1 = contents()->GetMainFrame(); + + // Send an update favicon message and make sure it works. + { + PluginFaviconMessageObserver observer(contents()); + EXPECT_TRUE(rfh1->GetRenderViewHost()->OnMessageReceived( + ViewHostMsg_UpdateFaviconURL( + rfh1->GetRenderViewHost()->GetRoutingID(), icons))); + EXPECT_TRUE(observer.favicon_received()); + } + + // Create one more frame in the same SiteInstance where |rfh1| exists so that + // it doesn't get deleted on navigation to another site. + rfh1->GetSiteInstance()->increment_active_frame_count(); + + // Navigate to a cross-site URL and commit the new page. + controller().LoadURL( + kDestUrl, Referrer(), ui::PAGE_TRANSITION_LINK, std::string()); + contents()->GetMainFrame()->PrepareForCommit(); + TestRenderFrameHost* rfh2 = contents()->GetPendingMainFrame(); + contents()->TestDidNavigate(rfh2, 1, kDestUrl, ui::PAGE_TRANSITION_TYPED); + EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT, rfh2->rfh_state()); + EXPECT_EQ(RenderFrameHostImpl::STATE_PENDING_SWAP_OUT, rfh1->rfh_state()); + + // The new RVH should be able to update its favicons. + { + PluginFaviconMessageObserver observer(contents()); + EXPECT_TRUE(rfh2->GetRenderViewHost()->OnMessageReceived( + ViewHostMsg_UpdateFaviconURL(rfh2->GetRenderViewHost()->GetRoutingID(), + icons))); + EXPECT_TRUE(observer.favicon_received()); + } + + // The old renderer, being slow, now updates its favicons. The message should + // be ignored. + { + PluginFaviconMessageObserver observer(contents()); + EXPECT_TRUE(rfh1->GetRenderViewHost()->OnMessageReceived( + ViewHostMsg_UpdateFaviconURL(rfh1->GetRenderViewHost()->GetRoutingID(), + icons))); + EXPECT_FALSE(observer.favicon_received()); + } +} + // Ensure that frames aren't added to the frame tree, if the message is coming // from a process different than the parent frame's current RenderFrameHost // process. Otherwise it is possible to have collisions of routing ids, as they
diff --git a/content/browser/geofencing/geofencing_manager.cc b/content/browser/geofencing/geofencing_manager.cc index 96f55ec..d446057 100644 --- a/content/browser/geofencing/geofencing_manager.cc +++ b/content/browser/geofencing/geofencing_manager.cc
@@ -110,7 +110,7 @@ // Look up service worker. ServiceWorkerRegistration* service_worker_registration = - service_worker_context_->context()->GetLiveRegistration( + service_worker_context_->GetLiveRegistration( service_worker_registration_id); if (!service_worker_registration) { callback.Run(GEOFENCING_STATUS_OPERATION_FAILED_NO_SERVICE_WORKER); @@ -146,7 +146,7 @@ // Look up service worker. ServiceWorkerRegistration* service_worker_registration = - service_worker_context_->context()->GetLiveRegistration( + service_worker_context_->GetLiveRegistration( service_worker_registration_id); if (!service_worker_registration) { callback.Run(GEOFENCING_STATUS_OPERATION_FAILED_NO_SERVICE_WORKER); @@ -185,7 +185,7 @@ // Look up service worker. ServiceWorkerRegistration* service_worker_registration = - service_worker_context_->context()->GetLiveRegistration( + service_worker_context_->GetLiveRegistration( service_worker_registration_id); if (!service_worker_registration) { return GEOFENCING_STATUS_OPERATION_FAILED_NO_SERVICE_WORKER; @@ -358,7 +358,7 @@ return; } - service_worker_context_->context()->storage()->FindRegistrationForId( + service_worker_context_->FindRegistrationForId( registration->service_worker_registration_id, registration->service_worker_origin, base::Bind(&GeofencingManager::DeliverGeofencingEvent,
diff --git a/content/browser/gpu/gpu_process_host_ui_shim.cc b/content/browser/gpu/gpu_process_host_ui_shim.cc index bafd049..1df45fc8 100644 --- a/content/browser/gpu/gpu_process_host_ui_shim.cc +++ b/content/browser/gpu/gpu_process_host_ui_shim.cc
@@ -288,7 +288,7 @@ // it to the GPU process immediately, so we can proceed to the next frame. bool should_not_show_frame = content::ImageTransportFactory::GetInstance() - ->SurfaceShouldNotShowFramesAfterRecycle(params.surface_id); + ->SurfaceShouldNotShowFramesAfterSuspendForRecycle(params.surface_id); if (should_not_show_frame) { OnSurfaceDisplayedCallback(params.surface_id); } else {
diff --git a/content/browser/indexed_db/indexed_db_browsertest.cc b/content/browser/indexed_db/indexed_db_browsertest.cc index 21f6ad6..9ad5b4d 100644 --- a/content/browser/indexed_db/indexed_db_browsertest.cc +++ b/content/browser/indexed_db/indexed_db_browsertest.cc
@@ -492,22 +492,14 @@ // => IndexedDBBackingStore::SetUpMetadata // #2: IndexedDBBackingStore::OpenBackingStore // => IndexedDBBackingStore::CleanUpBlobJournal (no-op) - // * Then deletes the database: - // #3: IndexedDBFactoryImpl::DeleteDatabase - // => IndexedDBDatabase::Create - // => IndexedDBBackingStore::CreateIDBDatabaseMetaData - // #4: IndexedDBFactoryImpl::DeleteDatabase - // => IndexedDBDatabase::DeleteDatabase - // => IndexedDBBackingStore::DeleteDatabase - // => IndexedDBBackingStore::CleanUpBlobJournal (no-op) // * The test calls open(), to create a new database: - // #5: IndexedDBFactoryImpl::Open + // #3: IndexedDBFactoryImpl::Open // => IndexedDBDatabase::Create // => IndexedDBBackingStore::CreateIDBDatabaseMetaData - // #6: IndexedDBTransaction::Commit - initial "versionchange" transaction + // #4: IndexedDBTransaction::Commit - initial "versionchange" transaction // * Once the connection is opened, the test runs: - // #7: IndexedDBTransaction::Commit - the test's "readwrite" transaction) - const int instance_num = 7; + // #5: IndexedDBTransaction::Commit - the test's "readwrite" transaction) + const int instance_num = 5; const int call_num = 1; FailOperation(FAIL_CLASS_LEVELDB_TRANSACTION, FAIL_METHOD_COMMIT_DISK_FULL, instance_num, call_num);
diff --git a/content/browser/indexed_db/indexed_db_database.cc b/content/browser/indexed_db/indexed_db_database.cc index b72ee73b..860fc0c0 100644 --- a/content/browser/indexed_db/indexed_db_database.cc +++ b/content/browser/indexed_db/indexed_db_database.cc
@@ -11,6 +11,7 @@ #include "base/logging.h" #include "base/memory/scoped_ptr.h" #include "base/memory/scoped_vector.h" +#include "base/metrics/histogram_macros.h" #include "base/stl_util.h" #include "base/strings/string_number_conversions.h" #include "base/strings/utf_string_conversions.h" @@ -37,6 +38,34 @@ namespace content { +namespace { + +// Used for WebCore.IndexedDB.Schema.ObjectStore.KeyPathType and +// WebCore.IndexedDB.Schema.Index.KeyPathType histograms. Do not +// modify (delete, re-order, renumber) these values other than +// the _MAX value. +enum HistogramIDBKeyPathType { + KEY_PATH_TYPE_NONE = 0, + KEY_PATH_TYPE_STRING = 1, + KEY_PATH_TYPE_ARRAY = 2, + KEY_PATH_TYPE_MAX = 3, // Keep as last/max entry, for histogram range. +}; + +HistogramIDBKeyPathType HistogramKeyPathType(const IndexedDBKeyPath& key_path) { + switch (key_path.type()) { + case blink::WebIDBKeyPathTypeNull: + return KEY_PATH_TYPE_NONE; + case blink::WebIDBKeyPathTypeString: + return KEY_PATH_TYPE_STRING; + case blink::WebIDBKeyPathTypeArray: + return KEY_PATH_TYPE_ARRAY; + } + NOTREACHED(); + return KEY_PATH_TYPE_NONE; +} + +} // namespace + // PendingUpgradeCall has a scoped_ptr<IndexedDBConnection> because it owns the // in-progress connection. class IndexedDBDatabase::PendingUpgradeCall { @@ -280,6 +309,11 @@ return; } + UMA_HISTOGRAM_ENUMERATION("WebCore.IndexedDB.Schema.ObjectStore.KeyPathType", + HistogramKeyPathType(key_path), KEY_PATH_TYPE_MAX); + UMA_HISTOGRAM_BOOLEAN("WebCore.IndexedDB.Schema.ObjectStore.AutoIncrement", + auto_increment); + // Store creation is done synchronously, as it may be followed by // index creation (also sync) since preemptive OpenCursor/SetIndexKeys // may follow. @@ -349,6 +383,12 @@ if (!ValidateObjectStoreIdAndNewIndexId(object_store_id, index_id)) return; + UMA_HISTOGRAM_ENUMERATION("WebCore.IndexedDB.Schema.Index.KeyPathType", + HistogramKeyPathType(key_path), KEY_PATH_TYPE_MAX); + UMA_HISTOGRAM_BOOLEAN("WebCore.IndexedDB.Schema.Index.Unique", unique); + UMA_HISTOGRAM_BOOLEAN("WebCore.IndexedDB.Schema.Index.MultiEntry", + multi_entry); + // Index creation is done synchronously since preemptive // OpenCursor/SetIndexKeys may follow. const IndexedDBIndexMetadata index_metadata(
diff --git a/content/browser/indexed_db/indexed_db_factory_impl.cc b/content/browser/indexed_db/indexed_db_factory_impl.cc index f818f3f..039cbddf 100644 --- a/content/browser/indexed_db/indexed_db_factory_impl.cc +++ b/content/browser/indexed_db/indexed_db_factory_impl.cc
@@ -257,6 +257,26 @@ return; } + std::vector<base::string16> names = backing_store->GetDatabaseNames(&s); + if (!s.ok()) { + DLOG(ERROR) << "Internal error getting database names"; + IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError, + "Internal error opening backing store for " + "indexedDB.deleteDatabase."); + callbacks->OnError(error); + backing_store = NULL; + if (s.IsCorruption()) + HandleBackingStoreCorruption(origin_url, error); + return; + } + if (!ContainsValue(names, name)) { + const int64 version = 0; + callbacks->OnSuccess(version); + backing_store = NULL; + ReleaseBackingStore(origin_url, false /* immediate */); + return; + } + scoped_refptr<IndexedDBDatabase> database = IndexedDBDatabase::Create( name, backing_store.get(), this, unique_identifier, &s); if (!database.get()) {
diff --git a/content/browser/loader/resource_dispatcher_host_impl.cc b/content/browser/loader/resource_dispatcher_host_impl.cc index 9631a50..8c86bc9 100644 --- a/content/browser/loader/resource_dispatcher_host_impl.cc +++ b/content/browser/loader/resource_dispatcher_host_impl.cc
@@ -2182,11 +2182,6 @@ if (info_map->empty()) return; - // TODO(pkasting): Remove ScopedTracker below once crbug.com/455952 is - // fixed. - tracked_objects::ScopedTracker tracking_profile( - FROM_HERE_WITH_EXPLICIT_FUNCTION("455952 BrowserThread::PostTask()")); - BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, base::Bind(&ResourceDispatcherHostImpl::UpdateLoadInfoOnUIThread,
diff --git a/content/browser/media/capture/content_video_capture_device_core.cc b/content/browser/media/capture/content_video_capture_device_core.cc index 3c507c6f..757db4a 100644 --- a/content/browser/media/capture/content_video_capture_device_core.cc +++ b/content/browser/media/capture/content_video_capture_device_core.cc
@@ -75,9 +75,9 @@ const gfx::Size coded_size((visible_size.width() + 15) & ~15, (visible_size.height() + 15) & ~15); - scoped_refptr<media::VideoCaptureDevice::Client::Buffer> output_buffer = + scoped_ptr<media::VideoCaptureDevice::Client::Buffer> output_buffer( client_->ReserveOutputBuffer(params_.requested_format.pixel_format, - coded_size); + coded_size)); const bool should_capture = oracle_.ObserveEventAndDecideCapture(event, damage_rect, event_time); const char* event_name = @@ -134,7 +134,7 @@ *callback = base::Bind(&ThreadSafeCaptureOracle::DidCaptureFrame, this, frame_number, - output_buffer, + base::Passed(&output_buffer), capture_begin_time); return true; } @@ -180,7 +180,7 @@ void ThreadSafeCaptureOracle::DidCaptureFrame( int frame_number, - const scoped_refptr<media::VideoCaptureDevice::Client::Buffer>& buffer, + scoped_ptr<media::VideoCaptureDevice::Client::Buffer> buffer, base::TimeTicks capture_begin_time, const scoped_refptr<media::VideoFrame>& frame, base::TimeTicks timestamp, @@ -202,7 +202,7 @@ media::VideoFrameMetadata::CAPTURE_BEGIN_TIME, capture_begin_time); frame->metadata()->SetTimeTicks( media::VideoFrameMetadata::CAPTURE_END_TIME, base::TimeTicks::Now()); - client_->OnIncomingCapturedVideoFrame(buffer, frame, timestamp); + client_->OnIncomingCapturedVideoFrame(buffer.Pass(), frame, timestamp); } } }
diff --git a/content/browser/media/capture/content_video_capture_device_core.h b/content/browser/media/capture/content_video_capture_device_core.h index 00ac6249d..f477a15 100644 --- a/content/browser/media/capture/content_video_capture_device_core.h +++ b/content/browser/media/capture/content_video_capture_device_core.h
@@ -86,7 +86,7 @@ // Callback invoked on completion of all captures. void DidCaptureFrame( int frame_number, - const scoped_refptr<media::VideoCaptureDevice::Client::Buffer>& buffer, + scoped_ptr<media::VideoCaptureDevice::Client::Buffer> buffer, base::TimeTicks capture_begin_time, const scoped_refptr<media::VideoFrame>& frame, base::TimeTicks timestamp,
diff --git a/content/browser/media/capture/desktop_capture_device_aura_unittest.cc b/content/browser/media/capture/desktop_capture_device_aura_unittest.cc index 4c69a57..054c933 100644 --- a/content/browser/media/capture/desktop_capture_device_aura_unittest.cc +++ b/content/browser/media/capture/desktop_capture_device_aura_unittest.cc
@@ -51,14 +51,28 @@ const media::VideoCaptureFormat& frame_format, int clockwise_rotation, const base::TimeTicks& timestamp)); - MOCK_METHOD2(ReserveOutputBuffer, - scoped_refptr<Buffer>(media::VideoPixelFormat format, - const gfx::Size& dimensions)); - MOCK_METHOD3(OnIncomingCapturedVideoFrame, - void(const scoped_refptr<Buffer>& buffer, - const scoped_refptr<media::VideoFrame>& frame, - const base::TimeTicks& timestamp)); + MOCK_METHOD0(DoReserveOutputBuffer, void(void)); + MOCK_METHOD0(DoOnIncomingCapturedBuffer, void(void)); + MOCK_METHOD0(DoOnIncomingCapturedVideoFrame, void(void)); MOCK_METHOD1(OnError, void(const std::string& reason)); + + // Trampoline methods to workaround GMOCK problems with scoped_ptr<>. + scoped_ptr<Buffer> ReserveOutputBuffer(media::VideoPixelFormat format, + const gfx::Size& dimensions) override { + DoReserveOutputBuffer(); + return scoped_ptr<Buffer>(); + } + void OnIncomingCapturedBuffer(scoped_ptr<Buffer> buffer, + const media::VideoCaptureFormat& frame_format, + const base::TimeTicks& timestamp) override { + DoOnIncomingCapturedBuffer(); + } + void OnIncomingCapturedVideoFrame( + scoped_ptr<Buffer> buffer, + const scoped_refptr<media::VideoFrame>& frame, + const base::TimeTicks& timestamp) override { + DoOnIncomingCapturedVideoFrame(); + } }; // Test harness that sets up a minimal environment with necessary stubs.
diff --git a/content/browser/media/capture/desktop_capture_device_unittest.cc b/content/browser/media/capture/desktop_capture_device_unittest.cc index 1a874f4..c86ddd38 100644 --- a/content/browser/media/capture/desktop_capture_device_unittest.cc +++ b/content/browser/media/capture/desktop_capture_device_unittest.cc
@@ -69,14 +69,28 @@ const media::VideoCaptureFormat& frame_format, int clockwise_rotation, const base::TimeTicks& timestamp)); - MOCK_METHOD2(ReserveOutputBuffer, - scoped_refptr<Buffer>(media::VideoPixelFormat format, - const gfx::Size& dimensions)); - MOCK_METHOD3(OnIncomingCapturedVideoFrame, - void(const scoped_refptr<Buffer>& buffer, - const scoped_refptr<media::VideoFrame>& frame, - const base::TimeTicks& timestamp)); + MOCK_METHOD0(DoReserveOutputBuffer, void(void)); + MOCK_METHOD0(DoOnIncomingCapturedBuffer, void(void)); + MOCK_METHOD0(DoOnIncomingCapturedVideoFrame, void(void)); MOCK_METHOD1(OnError, void(const std::string& reason)); + + // Trampoline methods to workaround GMOCK problems with scoped_ptr<>. + scoped_ptr<Buffer> ReserveOutputBuffer(media::VideoPixelFormat format, + const gfx::Size& dimensions) override { + DoReserveOutputBuffer(); + return scoped_ptr<Buffer>(); + } + void OnIncomingCapturedBuffer(scoped_ptr<Buffer> buffer, + const media::VideoCaptureFormat& frame_format, + const base::TimeTicks& timestamp) override { + DoOnIncomingCapturedBuffer(); + } + void OnIncomingCapturedVideoFrame( + scoped_ptr<Buffer> buffer, + const scoped_refptr<media::VideoFrame>& frame, + const base::TimeTicks& timestamp) override { + DoOnIncomingCapturedVideoFrame(); + } }; // Creates a DesktopFrame that has the first pixel bytes set to
diff --git a/content/browser/media/capture/web_contents_video_capture_device_unittest.cc b/content/browser/media/capture/web_contents_video_capture_device_unittest.cc index 8b24ed79..14fa1ff 100644 --- a/content/browser/media/capture/web_contents_video_capture_device_unittest.cc +++ b/content/browser/media/capture/web_contents_video_capture_device_unittest.cc
@@ -30,6 +30,7 @@ #include "media/base/video_util.h" #include "media/base/yuv_convert.h" #include "skia/ext/platform_canvas.h" +#include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/skia/include/core/SkColor.h" #include "ui/gfx/display.h" @@ -315,27 +316,26 @@ } ~StubClient() override {} - void OnIncomingCapturedData(const uint8* data, - int length, - const media::VideoCaptureFormat& frame_format, - int rotation, - const base::TimeTicks& timestamp) override { - FAIL(); - } + MOCK_METHOD5(OnIncomingCapturedData, + void(const uint8* data, + int length, + const media::VideoCaptureFormat& frame_format, + int rotation, + const base::TimeTicks& timestamp)); + MOCK_METHOD9(OnIncomingCapturedYuvData, + void (const uint8* y_data, + const uint8* u_data, + const uint8* 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)); - void OnIncomingCapturedYuvData(const uint8* y_data, - const uint8* u_data, - const uint8* 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 { - FAIL(); - } + MOCK_METHOD0(DoOnIncomingCapturedBuffer, void(void)); - scoped_refptr<media::VideoCaptureDevice::Client::Buffer> ReserveOutputBuffer( + scoped_ptr<media::VideoCaptureDevice::Client::Buffer> ReserveOutputBuffer( media::VideoPixelFormat format, const gfx::Size& dimensions) override { CHECK_EQ(format, media::PIXEL_FORMAT_I420); @@ -344,15 +344,20 @@ &buffer_id_to_drop); if (buffer_id == VideoCaptureBufferPool::kInvalidId) return NULL; - void* data; - size_t size; - buffer_pool_->GetBufferInfo(buffer_id, &data, &size); - return scoped_refptr<media::VideoCaptureDevice::Client::Buffer>( - new AutoReleaseBuffer(buffer_pool_, buffer_id, data, size)); + + return scoped_ptr<media::VideoCaptureDevice::Client::Buffer>( + new AutoReleaseBuffer( + buffer_pool_, buffer_pool_->GetBufferHandle(buffer_id), buffer_id)); + } + // Trampoline method to workaround GMOCK problems with scoped_ptr<>. + void OnIncomingCapturedBuffer(scoped_ptr<Buffer> buffer, + const media::VideoCaptureFormat& frame_format, + const base::TimeTicks& timestamp) override { + DoOnIncomingCapturedBuffer(); } void OnIncomingCapturedVideoFrame( - const scoped_refptr<Buffer>& buffer, + scoped_ptr<Buffer> buffer, const scoped_refptr<media::VideoFrame>& frame, const base::TimeTicks& timestamp) override { EXPECT_EQ(gfx::Size(kTestWidth, kTestHeight), frame->visible_rect().size()); @@ -376,27 +381,26 @@ private: class AutoReleaseBuffer : public media::VideoCaptureDevice::Client::Buffer { public: - AutoReleaseBuffer(const scoped_refptr<VideoCaptureBufferPool>& pool, - int buffer_id, - void* data, - size_t size) - : pool_(pool), - id_(buffer_id), - data_(data), - size_(size) { + AutoReleaseBuffer( + const scoped_refptr<VideoCaptureBufferPool>& pool, + scoped_ptr<VideoCaptureBufferPool::BufferHandle> buffer_handle, + int buffer_id) + : id_(buffer_id), + pool_(pool), + buffer_handle_(buffer_handle.Pass()) { DCHECK(pool_.get()); } int id() const override { return id_; } - void* data() const override { return data_; } - size_t size() const override { return size_; } + size_t size() const override { return buffer_handle_->size(); } + void* data() override { return buffer_handle_->data(); } + ClientBuffer AsClientBuffer() override { return nullptr; } private: ~AutoReleaseBuffer() override { pool_->RelinquishProducerReservation(id_); } - const scoped_refptr<VideoCaptureBufferPool> pool_; const int id_; - void* const data_; - const size_t size_; + const scoped_refptr<VideoCaptureBufferPool> pool_; + const scoped_ptr<VideoCaptureBufferPool::BufferHandle> buffer_handle_; }; scoped_refptr<VideoCaptureBufferPool> buffer_pool_;
diff --git a/content/browser/media/cdm/browser_cdm_manager.cc b/content/browser/media/cdm/browser_cdm_manager.cc index 3eb55f1..88cf526 100644 --- a/content/browser/media/cdm/browser_cdm_manager.cc +++ b/content/browser/media/cdm/browser_cdm_manager.cc
@@ -36,13 +36,6 @@ namespace { -// Maximum lengths for various EME API parameters. These are checks to -// prevent unnecessarily large parameters from being passed around, and the -// lengths are somewhat arbitrary as the EME spec doesn't specify any limits. -const size_t kMaxInitDataLength = 64 * 1024; // 64 KB -const size_t kMaxSessionResponseLength = 64 * 1024; // 64 KB -const size_t kMaxKeySystemLength = 256; - // The ID used in this class is a concatenation of |render_frame_id| and // |cdm_id|, i.e. (render_frame_id << 32) + cdm_id. @@ -300,7 +293,7 @@ int cdm_id, const std::string& key_system, const GURL& security_origin) { - if (key_system.size() > kMaxKeySystemLength) { + if (key_system.size() > media::limits::kMaxKeySystemLength) { // This failure will be discovered and reported by OnCreateSession() // as GetCdm() will return null. NOTREACHED() << "Invalid key system: " << key_system; @@ -346,7 +339,7 @@ scoped_ptr<NewSessionPromise> promise( new NewSessionPromise(this, render_frame_id, cdm_id, promise_id)); - if (init_data.size() > kMaxInitDataLength) { + if (init_data.size() > media::limits::kMaxInitDataLength) { LOG(WARNING) << "InitData for ID: " << cdm_id << " too long: " << init_data.size(); promise->reject(MediaKeys::INVALID_ACCESS_ERROR, 0, "Init data too long."); @@ -400,7 +393,7 @@ return; } - if (response.size() > kMaxSessionResponseLength) { + if (response.size() > media::limits::kMaxSessionResponseLength) { LOG(WARNING) << "Response for ID " << cdm_id << " is too long: " << response.size(); promise->reject(MediaKeys::INVALID_ACCESS_ERROR, 0, "Response too long.");
diff --git a/content/browser/media/media_internals_proxy.cc b/content/browser/media/media_internals_proxy.cc index 2181caa..05f4dd82 100644 --- a/content/browser/media/media_internals_proxy.cc +++ b/content/browser/media/media_internals_proxy.cc
@@ -122,7 +122,8 @@ MediaInternals::GetInstance()->AddUpdateCallback(update_callback_); if (GetContentClient()->browser()->GetNetLog()) { net::NetLog* net_log = GetContentClient()->browser()->GetNetLog(); - net_log->DeprecatedAddObserver(this, net::NetLog::LOG_ALL_BUT_BYTES); + net_log->DeprecatedAddObserver( + this, net::NetLogCaptureMode::IncludeCookiesAndCredentials()); } }
diff --git a/content/browser/mojo/service_registrar_android.cc b/content/browser/mojo/service_registrar_android.cc index 6e6928983..30ea74210 100644 --- a/content/browser/mojo/service_registrar_android.cc +++ b/content/browser/mojo/service_registrar_android.cc
@@ -23,4 +23,11 @@ env, registry->GetObj().obj(), base::android::GetApplicationContext()); } +// static +void ServiceRegistrarAndroid::RegisterFrameHostServices( + ServiceRegistryAndroid* registry) { + JNIEnv* env = base::android::AttachCurrentThread(); + Java_ServiceRegistrar_registerFrameHostServices( + env, registry->GetObj().obj(), base::android::GetApplicationContext()); +} } // namespace content
diff --git a/content/browser/mojo/service_registrar_android.h b/content/browser/mojo/service_registrar_android.h index ae81646..fdf62d4f 100644 --- a/content/browser/mojo/service_registrar_android.h +++ b/content/browser/mojo/service_registrar_android.h
@@ -17,6 +17,7 @@ public: static bool Register(JNIEnv* env); static void RegisterProcessHostServices(ServiceRegistryAndroid* registry); + static void RegisterFrameHostServices(ServiceRegistryAndroid* registry); }; } // namespace content
diff --git a/content/browser/navigator_connect/navigator_connect_service_worker_service_factory.cc b/content/browser/navigator_connect/navigator_connect_service_worker_service_factory.cc index 7f7d350..17ee080 100644 --- a/content/browser/navigator_connect/navigator_connect_service_worker_service_factory.cc +++ b/content/browser/navigator_connect/navigator_connect_service_worker_service_factory.cc
@@ -83,7 +83,7 @@ for (const auto& port : sent_message_ports) MessagePortService::GetInstance()->HoldMessages(port.id); - service_worker_context_->context()->storage()->FindRegistrationForId( + service_worker_context_->FindRegistrationForId( service_worker_registration_id_, service_worker_registration_origin_, base::Bind(&NavigatorConnectServiceWorkerService::DeliverMessage, weak_factory_.GetWeakPtr(), message.message_as_string, @@ -143,7 +143,7 @@ DCHECK_CURRENTLY_ON(BrowserThread::IO); // Find the right service worker to service this connection. - service_worker_context_->context()->storage()->FindRegistrationForDocument( + service_worker_context_->FindRegistrationForDocument( client.target_url, base::Bind(&NavigatorConnectServiceWorkerServiceFactory:: GotServiceWorkerRegistration,
diff --git a/content/browser/net/sqlite_persistent_cookie_store.cc b/content/browser/net/sqlite_persistent_cookie_store.cc index 60b7cff7..5da609b 100644 --- a/content/browser/net/sqlite_persistent_cookie_store.cc +++ b/content/browser/net/sqlite_persistent_cookie_store.cc
@@ -139,6 +139,10 @@ ~Backend() { DCHECK(!db_.get()) << "Close should have already been called."; DCHECK(num_pending_ == 0 && pending_.empty()); + + for (net::CanonicalCookie* cookie : cookies_) { + delete cookie; + } } // Database upgrade statements. @@ -249,6 +253,8 @@ // Temporary buffer for cookies loaded from DB. Accumulates cookies to reduce // the number of messages sent to the client runner. Sent back in response to // individual load requests for domain keys or when all loading completes. + // Ownership of the cookies in this vector is transferred to the client in + // response to individual load requests or when all loading completes. std::vector<net::CanonicalCookie*> cookies_; // Map of domain keys(eTLD+1) to domains/hosts that are to be loaded from DB.
diff --git a/content/browser/notifications/notification_event_dispatcher_impl.cc b/content/browser/notifications/notification_event_dispatcher_impl.cc index b111961..78d2a85 100644 --- a/content/browser/notifications/notification_event_dispatcher_impl.cc +++ b/content/browser/notifications/notification_event_dispatcher_impl.cc
@@ -134,7 +134,7 @@ return; } - service_worker_context->context()->storage()->FindRegistrationForId( + service_worker_context->FindRegistrationForId( notification_database_data.service_worker_registration_id, origin, base::Bind(&DispatchNotificationClickEventOnRegistration,
diff --git a/content/browser/push_messaging/push_messaging_message_filter.cc b/content/browser/push_messaging/push_messaging_message_filter.cc index 2817d544..939a761 100644 --- a/content/browser/push_messaging/push_messaging_message_filter.cc +++ b/content/browser/push_messaging/push_messaging_message_filter.cc
@@ -225,7 +225,7 @@ data.user_visible = user_visible; ServiceWorkerRegistration* service_worker_registration = - service_worker_context_->context()->GetLiveRegistration( + service_worker_context_->GetLiveRegistration( service_worker_registration_id); if (!service_worker_registration || !service_worker_registration->active_version()) { @@ -234,7 +234,7 @@ } data.requesting_origin = service_worker_registration->pattern().GetOrigin(); - service_worker_context_->context()->storage()->StoreUserData( + service_worker_context_->StoreRegistrationUserData( service_worker_registration_id, data.requesting_origin, kPushSenderIdServiceWorkerKey, @@ -255,7 +255,7 @@ data.user_visible = user_visible; ServiceWorkerRegistration* service_worker_registration = - service_worker_context_->context()->GetLiveRegistration( + service_worker_context_->GetLiveRegistration( service_worker_registration_id); if (!service_worker_registration) { SendRegisterError(data, PUSH_REGISTRATION_STATUS_NO_SERVICE_WORKER); @@ -282,7 +282,7 @@ const RegisterData& data, const std::string& sender_id) { DCHECK_CURRENTLY_ON(BrowserThread::IO); - service_worker_context_->context()->storage()->GetUserData( + service_worker_context_->GetRegistrationUserData( data.service_worker_registration_id, kPushRegistrationIdServiceWorkerKey, base::Bind(&PushMessagingMessageFilter::DidCheckForExistingRegistration, @@ -311,7 +311,7 @@ base::Bind(&Core::RegisterOnUI, base::Unretained(ui_core_.get()), data, sender_id)); } else { - service_worker_context_->context()->storage()->GetUserData( + service_worker_context_->GetRegistrationUserData( data.service_worker_registration_id, kPushSenderIdServiceWorkerKey, base::Bind(&PushMessagingMessageFilter::DidGetSenderIdFromStorage, @@ -403,7 +403,7 @@ const RegisterData& data, const std::string& push_registration_id) { DCHECK_CURRENTLY_ON(BrowserThread::IO); - service_worker_context_->context()->storage()->StoreUserData( + service_worker_context_->StoreRegistrationUserData( data.service_worker_registration_id, data.requesting_origin, kPushRegistrationIdServiceWorkerKey, @@ -474,14 +474,14 @@ int request_id, int64_t service_worker_registration_id) { DCHECK_CURRENTLY_ON(BrowserThread::IO); ServiceWorkerRegistration* service_worker_registration = - service_worker_context_->context()->GetLiveRegistration( + service_worker_context_->GetLiveRegistration( service_worker_registration_id); if (!service_worker_registration) { DidUnregister(request_id, PUSH_UNREGISTRATION_STATUS_NO_SERVICE_WORKER); return; } - service_worker_context_->context()->storage()->GetUserData( + service_worker_context_->GetRegistrationUserData( service_worker_registration_id, kPushRegistrationIdServiceWorkerKey, base::Bind( @@ -500,7 +500,7 @@ DCHECK_CURRENTLY_ON(BrowserThread::IO); if (service_worker_status == SERVICE_WORKER_OK) { - service_worker_context_->context()->storage()->GetUserData( + service_worker_context_->GetRegistrationUserData( service_worker_registration_id, kPushSenderIdServiceWorkerKey, base::Bind( @@ -625,7 +625,7 @@ PushUnregistrationStatus unregistration_status) { DCHECK_CURRENTLY_ON(BrowserThread::IO); - service_worker_context_->context()->storage()->ClearUserData( + service_worker_context_->ClearRegistrationUserData( service_worker_registration_id, kPushRegistrationIdServiceWorkerKey, base::Bind(&PushMessagingMessageFilter::DidClearRegistrationData, @@ -686,7 +686,7 @@ int64_t service_worker_registration_id) { DCHECK_CURRENTLY_ON(BrowserThread::IO); // TODO(johnme): Validate arguments? - service_worker_context_->context()->storage()->GetUserData( + service_worker_context_->GetRegistrationUserData( service_worker_registration_id, kPushRegistrationIdServiceWorkerKey, base::Bind(&PushMessagingMessageFilter::DidGetRegistration, @@ -754,7 +754,7 @@ bool user_visible) { DCHECK_CURRENTLY_ON(BrowserThread::IO); ServiceWorkerRegistration* service_worker_registration = - service_worker_context_->context()->GetLiveRegistration( + service_worker_context_->GetLiveRegistration( service_worker_registration_id); if (!service_worker_registration) { Send(new PushMessagingMsg_GetPermissionStatusError(request_id));
diff --git a/content/browser/push_messaging/push_messaging_router.cc b/content/browser/push_messaging/push_messaging_router.cc index 0c789147..76de068 100644 --- a/content/browser/push_messaging/push_messaging_router.cc +++ b/content/browser/push_messaging/push_messaging_router.cc
@@ -49,7 +49,7 @@ DCHECK_CURRENTLY_ON(BrowserThread::IO); // Try to acquire the registration from storage. If it's already live we'll // receive it right away. If not, it will be revived from storage. - service_worker_context->context()->storage()->FindRegistrationForId( + service_worker_context->FindRegistrationForId( service_worker_registration_id, origin, base::Bind(&PushMessagingRouter::FindServiceWorkerRegistrationCallback,
diff --git a/content/browser/quota_dispatcher_host.cc b/content/browser/quota_dispatcher_host.cc index 2ea8a5ec..3cbfee2 100644 --- a/content/browser/quota_dispatcher_host.cc +++ b/content/browser/quota_dispatcher_host.cc
@@ -118,8 +118,7 @@ DCHECK(dispatcher_host()); DCHECK(params_.storage_type == storage::kStorageTypeTemporary || - params_.storage_type == storage::kStorageTypePersistent || - params_.storage_type == storage::kStorageTypeSyncable); + params_.storage_type == storage::kStorageTypePersistent); if (params_.storage_type == storage::kStorageTypePersistent) { quota_manager()->GetUsageAndQuotaForWebApps( params_.origin_url, params_.storage_type,
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 0ecaaea..3569b40b 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
@@ -319,9 +319,11 @@ 1); } - latency->AddLatencyNumber(ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT, - latency_component_id_, ++last_event_id_); - latency->TraceEventType(WebInputEventTraits::GetName(event.type)); + latency->AddLatencyNumberWithTraceName( + ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT, + latency_component_id_, ++last_event_id_, + WebInputEventTraits::GetName(event.type)); + UpdateLatencyCoordinates(event, device_scale_factor_, latency); if (event.type == blink::WebInputEvent::GestureScrollBegin) {
diff --git a/content/browser/renderer_host/media/video_capture_buffer_pool.cc b/content/browser/renderer_host/media/video_capture_buffer_pool.cc index a336a7e..c5298c1 100644 --- a/content/browser/renderer_host/media/video_capture_buffer_pool.cc +++ b/content/browser/renderer_host/media/video_capture_buffer_pool.cc
@@ -7,6 +7,10 @@ #include "base/logging.h" #include "base/memory/scoped_ptr.h" #include "base/stl_util.h" +#include "content/browser/gpu/browser_gpu_memory_buffer_manager.h" +#include "content/common/gpu/client/gpu_memory_buffer_impl.h" +#include "content/public/browser/browser_thread.h" +#include "media/base/video_frame.h" using media::VideoFrame; @@ -21,8 +25,8 @@ VideoFrame::Format frame_format; } const kVideoPixelFormatToVideoFrameFormat[] = { {media::PIXEL_FORMAT_I420, VideoFrame::I420}, - {media::PIXEL_FORMAT_ARGB, VideoFrame::ARGB}, {media::PIXEL_FORMAT_TEXTURE, VideoFrame::NATIVE_TEXTURE}, + {media::PIXEL_FORMAT_GPUMEMORYBUFFER, VideoFrame::NATIVE_TEXTURE}, }; for (const auto& format_pair : kVideoPixelFormatToVideoFrameFormat) { @@ -34,15 +38,60 @@ return VideoFrame::UNKNOWN; } +// A simple holder of a memory-backed buffer and accesors to it. +class SimpleBufferHandle final : public VideoCaptureBufferPool::BufferHandle { + public: + SimpleBufferHandle(void* data, size_t size) : data_(data), size_(size) {} + ~SimpleBufferHandle() override {} + + size_t size() const override { return size_; } + void* data() override { return data_; } + ClientBuffer AsClientBuffer() override { return nullptr; } + + private: + void* const data_; + const size_t size_; +}; + +// A holder of a GpuMemoryBuffer-backed buffer, Map()ed on ctor and Unmap()ed on +// dtor. Holds a weak reference to its GpuMemoryBuffer. +// TODO(mcasas) Map()ed on ctor, or on first use? +class GpuMemoryBufferBufferHandle + final : public VideoCaptureBufferPool::BufferHandle { + public: + GpuMemoryBufferBufferHandle(gfx::GpuMemoryBuffer* gmb, size_t size) + : gmb_(gmb), + data_(new void* [GpuMemoryBufferImpl:: + NumberOfPlanesForGpuMemoryBufferFormat( + gmb_->GetFormat())]), + size_(size) { + DCHECK(gmb && !gmb_->IsMapped()); + gmb_->Map(data_.get()); + } + ~GpuMemoryBufferBufferHandle() override { gmb_->Unmap(); } + + size_t size() const override { return size_; } + void* data() override { return data_[0]; } + ClientBuffer AsClientBuffer() override { return gmb_->AsClientBuffer(); } + + private: + gfx::GpuMemoryBuffer* const gmb_; + scoped_ptr<void*[]> data_; + const size_t size_; +}; + // Tracker specifics for SharedMemory. class VideoCaptureBufferPool::SharedMemTracker final : public Tracker { public: SharedMemTracker(); - bool Init(VideoFrame::Format format, const gfx::Size& dimensions) override; - void* storage() override { return shared_memory_.memory(); } - size_t requested_size() override { return shared_memory_.requested_size(); } - size_t mapped_size() override { return shared_memory_.mapped_size(); } + + size_t mapped_size() const override { return shared_memory_.mapped_size(); } + + scoped_ptr<BufferHandle> GetBufferHandle() override { + return make_scoped_ptr( + new SimpleBufferHandle(shared_memory_.memory(), mapped_size())); + } bool ShareToProcess(base::ProcessHandle process_handle, base::SharedMemoryHandle* new_handle) override { @@ -54,23 +103,84 @@ base::SharedMemory shared_memory_; }; -VideoCaptureBufferPool::SharedMemTracker::SharedMemTracker() - : Tracker() {} +// Tracker specifics for GpuMemoryBuffer. Owns one GpuMemoryBuffer and its +// associated pixel dimensions. +class VideoCaptureBufferPool::GpuMemoryBufferTracker final : public Tracker { + public: + GpuMemoryBufferTracker(); + bool Init(VideoFrame::Format format, const gfx::Size& dimensions) override; + ~GpuMemoryBufferTracker() override; + + size_t mapped_size() const override { return packed_size_; } + scoped_ptr<BufferHandle> GetBufferHandle() override { + return make_scoped_ptr(new GpuMemoryBufferBufferHandle( + gpu_memory_buffer_.get(), packed_size_)); + } + + bool ShareToProcess(base::ProcessHandle process_handle, + base::SharedMemoryHandle* new_handle) override { + return true; + } + + private: + size_t packed_size_; + scoped_ptr<gfx::GpuMemoryBuffer> gpu_memory_buffer_; +}; + +VideoCaptureBufferPool::SharedMemTracker::SharedMemTracker() : Tracker() { +} bool VideoCaptureBufferPool::SharedMemTracker::Init( VideoFrame::Format format, const gfx::Size& dimensions) { + DVLOG(2) << "allocating ShMem of " << dimensions.ToString(); // Input |dimensions| can be 0x0 for trackers that do not require memory // backing. The allocated size is calculated using VideoFrame methods since // this will be the abstraction used to wrap the underlying data. - return shared_memory_.CreateAndMapAnonymous( - VideoFrame::AllocationSize(format, dimensions)); + set_pixel_count(dimensions.GetArea()); + const size_t byte_count = VideoFrame::AllocationSize(format, dimensions); + if (!byte_count) + return true; + return shared_memory_.CreateAndMapAnonymous(byte_count); +} + +VideoCaptureBufferPool::GpuMemoryBufferTracker::GpuMemoryBufferTracker() + : Tracker(), gpu_memory_buffer_(nullptr) {} + +VideoCaptureBufferPool::GpuMemoryBufferTracker::~GpuMemoryBufferTracker() { + if (gpu_memory_buffer_->IsMapped()) + gpu_memory_buffer_->Unmap(); +} + +bool VideoCaptureBufferPool::GpuMemoryBufferTracker::Init( + VideoFrame::Format format, + const gfx::Size& dimensions) { + DVLOG(2) << "allocating GMB for " << dimensions.ToString(); + // BrowserGpuMemoryBufferManager::current() may not be accessed on IO Thread. + DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::IO)); + DCHECK(BrowserGpuMemoryBufferManager::current()); + set_pixel_count(dimensions.GetArea()); + gpu_memory_buffer_ = + BrowserGpuMemoryBufferManager::current()->AllocateGpuMemoryBuffer( + dimensions, + gfx::GpuMemoryBuffer::BGRA_8888, + gfx::GpuMemoryBuffer::MAP); + DLOG_IF(ERROR, !gpu_memory_buffer_.get()) << "Allocating GpuMemoryBuffer"; + if (!gpu_memory_buffer_.get()) + return false; + int plane_sizes; + gpu_memory_buffer_->GetStride(&plane_sizes); + packed_size_ = plane_sizes * dimensions.height(); + return true; } //static scoped_ptr<VideoCaptureBufferPool::Tracker> -VideoCaptureBufferPool::Tracker::CreateTracker() { - return make_scoped_ptr(new SharedMemTracker()); +VideoCaptureBufferPool::Tracker::CreateTracker(bool use_gmb) { + if (!use_gmb) + return make_scoped_ptr(new SharedMemTracker()); + else + return make_scoped_ptr(new GpuMemoryBufferTracker()); } VideoCaptureBufferPool::Tracker::~Tracker() {} @@ -101,25 +211,22 @@ *memory_size = tracker->mapped_size(); return remote_handle; } - DPLOG(ERROR) << "Error mapping Shared Memory."; + DPLOG(ERROR) << "Error mapping Shared Memory"; return base::SharedMemoryHandle(); } -bool VideoCaptureBufferPool::GetBufferInfo(int buffer_id, - void** storage, - size_t* size) { +scoped_ptr<VideoCaptureBufferPool::BufferHandle> +VideoCaptureBufferPool::GetBufferHandle(int buffer_id) { base::AutoLock lock(lock_); Tracker* tracker = GetTracker(buffer_id); if (!tracker) { NOTREACHED() << "Invalid buffer_id."; - return false; + return scoped_ptr<BufferHandle>(); } DCHECK(tracker->held_by_producer()); - *storage = tracker->storage(); - *size = tracker->mapped_size(); - return true; + return tracker->GetBufferHandle(); } int VideoCaptureBufferPool::ReserveForProducer(media::VideoPixelFormat format, @@ -177,30 +284,28 @@ const gfx::Size& dimensions, int* buffer_id_to_drop) { DCHECK(format == media::PIXEL_FORMAT_I420 || - format == media::PIXEL_FORMAT_ARGB || - format == media::PIXEL_FORMAT_TEXTURE); + format == media::PIXEL_FORMAT_TEXTURE || + format == media::PIXEL_FORMAT_GPUMEMORYBUFFER ); lock_.AssertAcquired(); - const media::VideoFrame::Format frame_format = - VideoPixelFormatToVideoFrameFormat(format); - const size_t size_in_bytes = - VideoFrame::AllocationSize(frame_format, dimensions); + *buffer_id_to_drop = kInvalidId; + const size_t size_in_pixels = dimensions.GetArea(); // Look for a tracker that's allocated, big enough, and not in use. Track the // largest one that's not big enough, in case we have to reallocate a tracker. *buffer_id_to_drop = kInvalidId; - size_t realloc_size = 0; + size_t largest_size_in_pixels = 0; TrackerMap::iterator tracker_to_drop = trackers_.end(); for (TrackerMap::iterator it = trackers_.begin(); it != trackers_.end(); ++it) { Tracker* const tracker = it->second; if (!tracker->consumer_hold_count() && !tracker->held_by_producer()) { - if (tracker->requested_size() >= size_in_bytes) { + if (tracker->pixel_count() >= size_in_pixels) { // Existing tracker is big enough. Reuse it. tracker->set_held_by_producer(true); return it->first; } - if (tracker->requested_size() > realloc_size) { - realloc_size = tracker->requested_size(); + if (tracker->pixel_count() > largest_size_in_pixels) { + largest_size_in_pixels = tracker->pixel_count(); tracker_to_drop = it; } } @@ -220,9 +325,13 @@ // Create the new tracker. const int buffer_id = next_buffer_id_++; - scoped_ptr<Tracker> tracker = Tracker::CreateTracker(); - if (!tracker->Init(frame_format, dimensions)) + + scoped_ptr<Tracker> tracker = + Tracker::CreateTracker(format == media::PIXEL_FORMAT_GPUMEMORYBUFFER); + if (!tracker->Init(VideoPixelFormatToVideoFrameFormat(format), dimensions)) { + DLOG(ERROR) << "Error initializing Tracker"; return kInvalidId; + } tracker->set_held_by_producer(true); trackers_[buffer_id] = tracker.release();
diff --git a/content/browser/renderer_host/media/video_capture_buffer_pool.h b/content/browser/renderer_host/media/video_capture_buffer_pool.h index 2546499..f3c22f7 100644 --- a/content/browser/renderer_host/media/video_capture_buffer_pool.h +++ b/content/browser/renderer_host/media/video_capture_buffer_pool.h
@@ -9,7 +9,6 @@ #include "base/basictypes.h" #include "base/memory/ref_counted.h" -#include "base/memory/scoped_vector.h" #include "base/memory/shared_memory.h" #include "base/process/process.h" #include "base/synchronization/lock.h" @@ -17,13 +16,14 @@ #include "media/base/video_capture_types.h" #include "media/base/video_frame.h" #include "ui/gfx/geometry/size.h" +#include "ui/gfx/gpu_memory_buffer.h" namespace content { // A thread-safe class that does the bookkeeping and lifetime management for a // pool of pixel buffers cycled between an in-process producer (e.g. a // VideoCaptureDevice) and a set of out-of-process consumers. The pool is -// intended to be orchestrated by a VideoCaptureController, but is designed +// intended to be orchestrated by a VideoCaptureDevice::Client, but is designed // to outlive the controller if necessary. The pixel buffers may be backed by a // SharedMemory, but this is not compulsory. // @@ -44,6 +44,16 @@ : public base::RefCountedThreadSafe<VideoCaptureBufferPool> { public: static const int kInvalidId; + + // Abstraction of a pool's buffer data buffer and size for clients. + class BufferHandle { + public: + virtual ~BufferHandle() {} + virtual size_t size() const = 0; + virtual void* data() = 0; + virtual ClientBuffer AsClientBuffer() = 0; + }; + explicit VideoCaptureBufferPool(int count); // One-time (per client/per-buffer) initialization to share a particular @@ -53,9 +63,8 @@ base::ProcessHandle process_handle, size_t* memory_size); - // Query the memory parameters of |buffer_id|. Fills in parameters in the - // pointer arguments, and returns true iff the buffer exists. - bool GetBufferInfo(int buffer_id, void** storage, size_t* size); + // Try and obtain a BufferHandle for |buffer_id|. + scoped_ptr<BufferHandle> GetBufferHandle(int buffer_id); // Reserve or allocate a buffer to support a packed frame of |dimensions| of // pixel |format| and return its id. This will fail (returning kInvalidId) if @@ -90,35 +99,37 @@ void RelinquishConsumerHold(int buffer_id, int num_clients); private: + class GpuMemoryBufferTracker; class SharedMemTracker; // Generic class to keep track of the state of a given mappable resource. class Tracker { public: - static scoped_ptr<Tracker> CreateTracker(); + static scoped_ptr<Tracker> CreateTracker(bool use_gmb); - Tracker() : held_by_producer_(false), consumer_hold_count_(0) {} + Tracker() + : pixel_count_(0), held_by_producer_(false), consumer_hold_count_(0) {} virtual bool Init(media::VideoFrame::Format format, const gfx::Size& dimensions) = 0; virtual ~Tracker(); + size_t pixel_count() const { return pixel_count_; } + void set_pixel_count(size_t count) { pixel_count_ = count; } bool held_by_producer() const { return held_by_producer_; } void set_held_by_producer(bool value) { held_by_producer_ = value; } int consumer_hold_count() const { return consumer_hold_count_; } void set_consumer_hold_count(int value) { consumer_hold_count_ = value; } - // Returns a void* to the underlying storage, be that a memory block for - // Shared Memory, or a GpuMemoryBuffer. - virtual void* storage() = 0; - // Amount of bytes requested when first created. Can be zero if it does not - // need RAM, e.g. is allocated in GPU memory. - virtual size_t requested_size() = 0; + // Returns a handle to the underlying storage, be that a block of Shared + // Memory, or a GpuMemoryBuffer. + virtual scoped_ptr<BufferHandle> GetBufferHandle() = 0; // The actual size of the underlying backing resource. - virtual size_t mapped_size() = 0; + virtual size_t mapped_size() const = 0; virtual bool ShareToProcess(base::ProcessHandle process_handle, base::SharedMemoryHandle* new_handle) = 0; private: + size_t pixel_count_; // Indicates whether this Tracker is currently referenced by the producer. bool held_by_producer_; // Number of consumer processes which hold this Tracker.
diff --git a/content/browser/renderer_host/media/video_capture_buffer_pool_unittest.cc b/content/browser/renderer_host/media/video_capture_buffer_pool_unittest.cc index e8f446f..ab3814ae 100644 --- a/content/browser/renderer_host/media/video_capture_buffer_pool_unittest.cc +++ b/content/browser/renderer_host/media/video_capture_buffer_pool_unittest.cc
@@ -9,139 +9,245 @@ #include "base/bind.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" +#include "cc/test/test_context_provider.h" +#include "cc/test/test_web_graphics_context_3d.h" +#include "content/browser/compositor/buffer_queue.h" +#include "content/browser/gpu/browser_gpu_memory_buffer_manager.h" #include "content/browser/renderer_host/media/video_capture_controller.h" #include "media/base/video_frame.h" #include "media/base/video_util.h" +#include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" namespace content { -class VideoCaptureBufferPoolTest : public testing::Test { +static const media::VideoPixelFormat kCaptureFormats[] = { + media::PIXEL_FORMAT_I420, + media::PIXEL_FORMAT_TEXTURE, +#if !defined(OS_ANDROID) + media::PIXEL_FORMAT_GPUMEMORYBUFFER +#endif +}; + +class VideoCaptureBufferPoolTest + : public testing::TestWithParam<media::VideoPixelFormat> { protected: + // A GpuMemoryBuffer Mock to provide a trivial RGBA buffer as Map() backing. + // We need to allocate on ctor and deallocate on dtor so that consecutive + // Map()-Unmap() cycles yield the same underlying data pointer. + class MockGpuMemoryBuffer : public gfx::GpuMemoryBuffer { + public: + explicit MockGpuMemoryBuffer(const gfx::Size& size) + : size_(size), data_(new uint8[size_.GetArea() * 4]), mapped_(false) {} + ~MockGpuMemoryBuffer() override { delete[] data_; } + + bool Map(void** data) override { + EXPECT_EQ(mapped_, false); + mapped_ = true; + data[0] = static_cast<void*>(data_); + return true; + } + void Unmap() override { + EXPECT_EQ(mapped_, true); + mapped_ = false; + } + bool IsMapped() const override { return mapped_; } + Format GetFormat() const override { return BGRA_8888; } + void GetStride(int* stride) const override { + *stride = size_.width() * 4; + return; + } + gfx::GpuMemoryBufferHandle GetHandle() const override { + return gfx::GpuMemoryBufferHandle(); + } + ClientBuffer AsClientBuffer() override { return nullptr; } + + private: + const gfx::Size size_; + uint8* const data_; + bool mapped_; + }; + +#if !defined(OS_ANDROID) + // The next two classes are needed to replicate the GpuMemoryBuffer allocation + // on Browser side. + class StubBrowserGpuMemoryBufferManager + : public BrowserGpuMemoryBufferManager { + public: + StubBrowserGpuMemoryBufferManager() + : BrowserGpuMemoryBufferManager(nullptr, 1) {} + + scoped_ptr<gfx::GpuMemoryBuffer> AllocateGpuMemoryBuffer( + const gfx::Size& size, + gfx::GpuMemoryBuffer::Format format, + gfx::GpuMemoryBuffer::Usage usage) override { + return make_scoped_ptr(new MockGpuMemoryBuffer(size)); + } + }; + class MockBufferQueue : public BufferQueue { + public: + MockBufferQueue(scoped_refptr<cc::ContextProvider> context_provider, + BrowserGpuMemoryBufferManager* gpu_memory_buffer_manager, + unsigned int internalformat) + : BufferQueue(context_provider, + internalformat, + nullptr, + gpu_memory_buffer_manager, + 1) {} + MOCK_METHOD4(CopyBufferDamage, + void(int, int, const gfx::Rect&, const gfx::Rect&)); + }; +#endif + + // This is a generic Buffer tracker class Buffer { public: Buffer(const scoped_refptr<VideoCaptureBufferPool> pool, - int id, - void* data, - size_t size) - : pool_(pool), id_(id), data_(data), size_(size) {} + scoped_ptr<VideoCaptureBufferPool::BufferHandle> buffer_handle, + int id) + : id_(id), pool_(pool), buffer_handle_(buffer_handle.Pass()) {} ~Buffer() { pool_->RelinquishProducerReservation(id()); } int id() const { return id_; } - void* data() const { return data_; } - size_t size() const { return size_; } + size_t size() { return buffer_handle_->size(); } + void* data() { return buffer_handle_->data(); } private: - const scoped_refptr<VideoCaptureBufferPool> pool_; const int id_; - void* const data_; - const size_t size_; + const scoped_refptr<VideoCaptureBufferPool> pool_; + const scoped_ptr<VideoCaptureBufferPool::BufferHandle> buffer_handle_; }; + VideoCaptureBufferPoolTest() : expected_dropped_id_(0), pool_(new VideoCaptureBufferPool(3)) {} +#if !defined(OS_ANDROID) + void SetUp() override { + scoped_refptr<cc::TestContextProvider> context_provider = + cc::TestContextProvider::Create(cc::TestWebGraphicsContext3D::Create()); + context_provider->BindToCurrentThread(); + gpu_memory_buffer_manager_.reset(new StubBrowserGpuMemoryBufferManager); + output_surface_.reset(new MockBufferQueue( + context_provider, gpu_memory_buffer_manager_.get(), GL_RGBA)); + output_surface_->Initialize(); + } +#endif + void ExpectDroppedId(int expected_dropped_id) { expected_dropped_id_ = expected_dropped_id; } - scoped_ptr<Buffer> ReserveI420Buffer(const gfx::Size& dimensions) { - // To verify that ReserveI420Buffer always sets |buffer_id_to_drop|, + scoped_ptr<Buffer> ReserveBuffer(const gfx::Size& dimensions, + media::VideoPixelFormat pixel_format) { + // To verify that ReserveBuffer always sets |buffer_id_to_drop|, // initialize it to something different than the expected value. int buffer_id_to_drop = ~expected_dropped_id_; - int buffer_id = pool_->ReserveForProducer(media::PIXEL_FORMAT_I420, - dimensions, &buffer_id_to_drop); + DVLOG(1) << media::VideoCaptureFormat::PixelFormatToString(pixel_format) + << " " << dimensions.ToString(); + int buffer_id = + pool_->ReserveForProducer(pixel_format, dimensions, &buffer_id_to_drop); if (buffer_id == VideoCaptureBufferPool::kInvalidId) return scoped_ptr<Buffer>(); - - void* memory; - size_t size; - pool_->GetBufferInfo(buffer_id, &memory, &size); EXPECT_EQ(expected_dropped_id_, buffer_id_to_drop); - return scoped_ptr<Buffer>(new Buffer(pool_, buffer_id, memory, size)); + + scoped_ptr<VideoCaptureBufferPool::BufferHandle> buffer_handle = + pool_->GetBufferHandle(buffer_id); + return scoped_ptr<Buffer>( + new Buffer(pool_, buffer_handle.Pass(), buffer_id)); } int expected_dropped_id_; scoped_refptr<VideoCaptureBufferPool> pool_; private: +#if !defined(OS_ANDROID) + scoped_ptr<StubBrowserGpuMemoryBufferManager> gpu_memory_buffer_manager_; + scoped_ptr<MockBufferQueue> output_surface_; +#endif + DISALLOW_COPY_AND_ASSIGN(VideoCaptureBufferPoolTest); }; -TEST_F(VideoCaptureBufferPoolTest, BufferPool) { - const gfx::Size size_lo = gfx::Size(640, 480); - const gfx::Size size_hi = gfx::Size(1024, 768); - scoped_refptr<media::VideoFrame> non_pool_frame = - media::VideoFrame::CreateFrame(media::VideoFrame::YV12, size_lo, - gfx::Rect(size_lo), size_lo, - base::TimeDelta()); +TEST_P(VideoCaptureBufferPoolTest, BufferPool) { + const gfx::Size size_lo = gfx::Size(10, 10); + const gfx::Size size_hi = gfx::Size(21, 33); + const media::VideoCaptureFormat format_lo(size_lo, 0.0, GetParam()); + const media::VideoCaptureFormat format_hi(size_hi, 0.0, GetParam()); // Reallocation won't happen for the first part of the test. ExpectDroppedId(VideoCaptureBufferPool::kInvalidId); - scoped_ptr<Buffer> buffer1 = ReserveI420Buffer(size_lo); - ASSERT_TRUE(NULL != buffer1.get()); - ASSERT_LE(media::VideoFrame::AllocationSize(media::VideoFrame::I420, size_lo), - buffer1->size()); - scoped_ptr<Buffer> buffer2 = ReserveI420Buffer(size_lo); - ASSERT_TRUE(NULL != buffer2.get()); - ASSERT_LE(media::VideoFrame::AllocationSize(media::VideoFrame::I420, size_lo), - buffer2->size()); - scoped_ptr<Buffer> buffer3 = ReserveI420Buffer(size_lo); - ASSERT_TRUE(NULL != buffer3.get()); - ASSERT_LE(media::VideoFrame::AllocationSize(media::VideoFrame::I420, size_lo), - buffer3->size()); + scoped_ptr<Buffer> buffer1 = ReserveBuffer(size_lo, GetParam()); + ASSERT_NE(nullptr, buffer1.get()); + ASSERT_LE(format_lo.ImageAllocationSize(), buffer1->size()); + scoped_ptr<Buffer> buffer2 = ReserveBuffer(size_lo, GetParam()); + ASSERT_NE(nullptr, buffer2.get()); + ASSERT_LE(format_lo.ImageAllocationSize(), buffer2->size()); + scoped_ptr<Buffer> buffer3 = ReserveBuffer(size_lo, GetParam()); + ASSERT_NE(nullptr, buffer3.get()); + ASSERT_LE(format_lo.ImageAllocationSize(), buffer3->size()); + // Texture backed Frames cannot be manipulated via mapping. + if (GetParam() != media::PIXEL_FORMAT_TEXTURE) { + ASSERT_NE(nullptr, buffer1->data()); + ASSERT_NE(nullptr, buffer2->data()); + ASSERT_NE(nullptr, buffer3->data()); + + } // Touch the memory. - memset(buffer1->data(), 0x11, buffer1->size()); - memset(buffer2->data(), 0x44, buffer2->size()); - memset(buffer3->data(), 0x77, buffer3->size()); + if (buffer1->data() != nullptr) + memset(buffer1->data(), 0x11, buffer1->size()); + if (buffer2->data() != nullptr) + memset(buffer2->data(), 0x44, buffer2->size()); + if (buffer3->data() != nullptr) + memset(buffer3->data(), 0x77, buffer3->size()); // Fourth buffer should fail. - ASSERT_FALSE(ReserveI420Buffer(size_lo)) << "Pool should be empty"; + ASSERT_FALSE(ReserveBuffer(size_lo, GetParam())) << "Pool should be empty"; // Release 1st buffer and retry; this should succeed. buffer1.reset(); - scoped_ptr<Buffer> buffer4 = ReserveI420Buffer(size_lo); - ASSERT_TRUE(NULL != buffer4.get()); + scoped_ptr<Buffer> buffer4 = ReserveBuffer(size_lo, GetParam()); + ASSERT_NE(nullptr, buffer4.get()); - ASSERT_FALSE(ReserveI420Buffer(size_lo)) << "Pool should be empty"; - ASSERT_FALSE(ReserveI420Buffer(size_hi)) << "Pool should be empty"; + ASSERT_FALSE(ReserveBuffer(size_lo, GetParam())) << "Pool should be empty"; + ASSERT_FALSE(ReserveBuffer(size_hi, GetParam())) << "Pool should be empty"; // Validate the IDs int buffer_id2 = buffer2->id(); ASSERT_EQ(1, buffer_id2); - int buffer_id3 = buffer3->id(); + const int buffer_id3 = buffer3->id(); ASSERT_EQ(2, buffer_id3); - void* const memory_pointer3 = buffer3->data(); - int buffer_id4 = buffer4->id(); + const int buffer_id4 = buffer4->id(); ASSERT_EQ(0, buffer_id4); + void* const memory_pointer3 = buffer3->data(); // Deliver a buffer. pool_->HoldForConsumers(buffer_id3, 2); - ASSERT_FALSE(ReserveI420Buffer(size_lo)) << "Pool should be empty"; + ASSERT_FALSE(ReserveBuffer(size_lo, GetParam())) << "Pool should be empty"; buffer3.reset(); // Old producer releases buffer. Should be a noop. - ASSERT_FALSE(ReserveI420Buffer(size_lo)) << "Pool should be empty"; - ASSERT_FALSE(ReserveI420Buffer(size_hi)) << "Pool should be empty"; + ASSERT_FALSE(ReserveBuffer(size_lo, GetParam())) << "Pool should be empty"; + ASSERT_FALSE(ReserveBuffer(size_hi, GetParam())) << "Pool should be empty"; buffer2.reset(); // Active producer releases buffer. Should free a buffer. - buffer1 = ReserveI420Buffer(size_lo); - ASSERT_TRUE(NULL != buffer1.get()); - ASSERT_FALSE(ReserveI420Buffer(size_lo)) << "Pool should be empty"; + buffer1 = ReserveBuffer(size_lo, GetParam()); + ASSERT_NE(nullptr, buffer1.get()); + ASSERT_FALSE(ReserveBuffer(size_lo, GetParam())) << "Pool should be empty"; // First consumer finishes. pool_->RelinquishConsumerHold(buffer_id3, 1); - ASSERT_FALSE(ReserveI420Buffer(size_lo)) << "Pool should be empty"; + ASSERT_FALSE(ReserveBuffer(size_lo, GetParam())) << "Pool should be empty"; // Second consumer finishes. This should free that buffer. pool_->RelinquishConsumerHold(buffer_id3, 1); - buffer3 = ReserveI420Buffer(size_lo); - ASSERT_TRUE(NULL != buffer3.get()); + buffer3 = ReserveBuffer(size_lo, GetParam()); + ASSERT_NE(nullptr, buffer3.get()); ASSERT_EQ(buffer_id3, buffer3->id()) << "Buffer ID should be reused."; ASSERT_EQ(memory_pointer3, buffer3->data()); - ASSERT_FALSE(ReserveI420Buffer(size_lo)) << "Pool should be empty"; + ASSERT_FALSE(ReserveBuffer(size_lo, GetParam())) << "Pool should be empty"; // Now deliver & consume buffer1, but don't release the buffer. int buffer_id1 = buffer1->id(); @@ -153,35 +259,33 @@ // be re-allocated to the producer, because |buffer1| still references it. But // when |buffer1| goes away, we should be able to re-reserve the buffer (and // the ID ought to be the same). - ASSERT_FALSE(ReserveI420Buffer(size_lo)) << "Pool should be empty"; + ASSERT_FALSE(ReserveBuffer(size_lo, GetParam())) << "Pool should be empty"; buffer1.reset(); // Should free the buffer. - buffer2 = ReserveI420Buffer(size_lo); - ASSERT_TRUE(NULL != buffer2.get()); + buffer2 = ReserveBuffer(size_lo, GetParam()); + ASSERT_NE(nullptr, buffer2.get()); ASSERT_EQ(buffer_id1, buffer2->id()); buffer_id2 = buffer_id1; - ASSERT_FALSE(ReserveI420Buffer(size_lo)) << "Pool should be empty"; + ASSERT_FALSE(ReserveBuffer(size_lo, GetParam())) << "Pool should be empty"; // Now try reallocation with different resolutions. We expect reallocation // to occur only when the old buffer is too small. buffer2.reset(); ExpectDroppedId(buffer_id2); - buffer2 = ReserveI420Buffer(size_hi); - ASSERT_TRUE(NULL != buffer2.get()); - ASSERT_LE(media::VideoFrame::AllocationSize(media::VideoFrame::I420, size_hi), - buffer2->size()); + buffer2 = ReserveBuffer(size_hi, GetParam()); + ASSERT_NE(nullptr, buffer2.get()); + ASSERT_LE(format_hi.ImageAllocationSize(), buffer2->size()); ASSERT_EQ(3, buffer2->id()); void* const memory_pointer_hi = buffer2->data(); buffer2.reset(); // Frees it. ExpectDroppedId(VideoCaptureBufferPool::kInvalidId); - buffer2 = ReserveI420Buffer(size_lo); + buffer2 = ReserveBuffer(size_lo, GetParam()); void* const memory_pointer_lo = buffer2->data(); ASSERT_EQ(memory_pointer_hi, memory_pointer_lo) << "Decrease in resolution should not reallocate buffer"; - ASSERT_TRUE(NULL != buffer2.get()); + ASSERT_NE(nullptr, buffer2.get()); ASSERT_EQ(3, buffer2->id()); - ASSERT_LE(media::VideoFrame::AllocationSize(media::VideoFrame::I420, size_lo), - buffer2->size()); - ASSERT_FALSE(ReserveI420Buffer(size_lo)) << "Pool should be empty"; + ASSERT_LE(format_lo.ImageAllocationSize(), buffer2->size()); + ASSERT_FALSE(ReserveBuffer(size_lo, GetParam())) << "Pool should be empty"; // Tear down the pool_, writing into the buffers. The buffer should preserve // the lifetime of the underlying memory. @@ -189,13 +293,19 @@ pool_ = NULL; // Touch the memory. - memset(buffer2->data(), 0x22, buffer2->size()); - memset(buffer4->data(), 0x55, buffer4->size()); - + if (buffer2->data() != nullptr) + memset(buffer2->data(), 0x22, buffer2->size()); + if (buffer4->data() != nullptr) + memset(buffer4->data(), 0x55, buffer4->size()); buffer2.reset(); - memset(buffer4->data(), 0x77, buffer4->size()); + if (buffer4->data() != nullptr) + memset(buffer4->data(), 0x77, buffer4->size()); buffer4.reset(); } +INSTANTIATE_TEST_CASE_P(, + VideoCaptureBufferPoolTest, + testing::ValuesIn(kCaptureFormats)); + } // namespace content
diff --git a/content/browser/renderer_host/media/video_capture_controller.cc b/content/browser/renderer_host/media/video_capture_controller.cc index a2c4b193..56b281be 100644 --- a/content/browser/renderer_host/media/video_capture_controller.cc +++ b/content/browser/renderer_host/media/video_capture_controller.cc
@@ -26,10 +26,6 @@ #include "content/browser/compositor/image_transport_factory.h" #endif -#if defined(ENABLE_WEBRTC) && (defined(OS_LINUX) || defined(OS_MACOSX)) -#include "content/browser/renderer_host/media/video_capture_texture_wrapper.h" -#endif - using media::VideoCaptureFormat; using media::VideoFrame; @@ -141,21 +137,10 @@ scoped_ptr<media::VideoCaptureDevice::Client> VideoCaptureController::NewDeviceClient( - const scoped_refptr<base::SingleThreadTaskRunner>& capture_task_runner, - const media::VideoCaptureFormat& format) { + const scoped_refptr<base::SingleThreadTaskRunner>& capture_task_runner) { DCHECK_CURRENTLY_ON(BrowserThread::IO); -#if defined(ENABLE_WEBRTC) && (defined(OS_LINUX) || defined(OS_MACOSX)) - if (base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kEnableWebRtcCaptureToTexture)) { - return make_scoped_ptr(new VideoCaptureTextureWrapper( - this->GetWeakPtrForIOThread(), buffer_pool_, capture_task_runner, - format)); - DVLOG(1) << "TextureWrapper, format " << format.ToString(); - } -#endif - return make_scoped_ptr( - new VideoCaptureDeviceClient(this->GetWeakPtrForIOThread(), - buffer_pool_)); + return make_scoped_ptr(new VideoCaptureDeviceClient( + this->GetWeakPtrForIOThread(), buffer_pool_, capture_task_runner)); } void VideoCaptureController::AddClient( @@ -287,11 +272,12 @@ } void VideoCaptureController::DoIncomingCapturedVideoFrameOnIOThread( - const scoped_refptr<media::VideoCaptureDevice::Client::Buffer>& buffer, + scoped_ptr<media::VideoCaptureDevice::Client::Buffer> buffer, const scoped_refptr<VideoFrame>& frame, const base::TimeTicks& timestamp) { DCHECK_CURRENTLY_ON(BrowserThread::IO); - DCHECK_NE(buffer->id(), VideoCaptureBufferPool::kInvalidId); + const int buffer_id = buffer->id(); + DCHECK_NE(buffer_id, VideoCaptureBufferPool::kInvalidId); int count = 0; if (state_ == VIDEO_CAPTURE_STATE_STARTED) { @@ -316,24 +302,24 @@ DCHECK(frame->coded_size() == frame->visible_rect().size()) << "Textures are always supposed to be tightly packed."; client->event_handler->OnMailboxBufferReady(client->controller_id, - buffer->id(), + buffer_id, *frame->mailbox_holder(), frame->coded_size(), timestamp, copy_of_metadata.Pass()); } else if (frame->format() == media::VideoFrame::I420) { - bool is_new_buffer = client->known_buffers.insert(buffer->id()).second; + bool is_new_buffer = client->known_buffers.insert(buffer_id).second; if (is_new_buffer) { // On the first use of a buffer on a client, share the memory handle. size_t memory_size = 0; base::SharedMemoryHandle remote_handle = buffer_pool_->ShareToProcess( - buffer->id(), client->render_process_handle, &memory_size); + buffer_id, client->render_process_handle, &memory_size); client->event_handler->OnBufferCreated( - client->controller_id, remote_handle, memory_size, buffer->id()); + client->controller_id, remote_handle, memory_size, buffer_id); } client->event_handler->OnBufferReady( - client->controller_id, buffer->id(), frame->coded_size(), + client->controller_id, buffer_id, frame->coded_size(), frame->visible_rect(), timestamp, copy_of_metadata.Pass()); } else { // VideoFrame format not supported. @@ -342,9 +328,9 @@ } bool inserted = - client->active_buffers.insert(std::make_pair(buffer->id(), frame)) + client->active_buffers.insert(std::make_pair(buffer_id, frame)) .second; - DCHECK(inserted) << "Unexpected duplicate buffer: " << buffer->id(); + DCHECK(inserted) << "Unexpected duplicate buffer: " << buffer_id; count++; } } @@ -365,7 +351,7 @@ has_received_frames_ = true; } - buffer_pool_->HoldForConsumers(buffer->id(), count); + buffer_pool_->HoldForConsumers(buffer_id, count); } void VideoCaptureController::DoErrorOnIOThread() {
diff --git a/content/browser/renderer_host/media/video_capture_controller.h b/content/browser/renderer_host/media/video_capture_controller.h index 2954688b..0dd8684 100644 --- a/content/browser/renderer_host/media/video_capture_controller.h +++ b/content/browser/renderer_host/media/video_capture_controller.h
@@ -72,8 +72,7 @@ // instance. Some device clients need to allocate resources for the given // capture |format| and/or work on Capture Thread (|capture_task_runner|). scoped_ptr<media::VideoCaptureDevice::Client> NewDeviceClient( - const scoped_refptr<base::SingleThreadTaskRunner>& capture_task_runner, - const media::VideoCaptureFormat& format); + const scoped_refptr<base::SingleThreadTaskRunner>& capture_task_runner); // Start video capturing and try to use the resolution specified in |params|. // Buffers will be shared to the client as necessary. The client will continue @@ -121,7 +120,7 @@ // Worker functions on IO thread. Called by the VideoCaptureDeviceClient. void DoIncomingCapturedVideoFrameOnIOThread( - const scoped_refptr<media::VideoCaptureDevice::Client::Buffer>& buffer, + scoped_ptr<media::VideoCaptureDevice::Client::Buffer> buffer, const scoped_refptr<media::VideoFrame>& frame, const base::TimeTicks& timestamp); void DoErrorOnIOThread();
diff --git a/content/browser/renderer_host/media/video_capture_controller_unittest.cc b/content/browser/renderer_host/media/video_capture_controller_unittest.cc index 2e6299a..ad9ecd4 100644 --- a/content/browser/renderer_host/media/video_capture_controller_unittest.cc +++ b/content/browser/renderer_host/media/video_capture_controller_unittest.cc
@@ -12,6 +12,7 @@ #include "base/memory/scoped_ptr.h" #include "base/message_loop/message_loop.h" #include "base/run_loop.h" +#include "base/thread_task_runner_handle.h" #include "content/browser/renderer_host/media/media_stream_provider.h" #include "content/browser/renderer_host/media/video_capture_controller.h" #include "content/browser/renderer_host/media/video_capture_controller_event_handler.h" @@ -122,8 +123,7 @@ void SetUp() override { controller_.reset(new VideoCaptureController(kPoolSize)); device_ = controller_->NewDeviceClient( - base::MessageLoopProxy::current(), - controller_->GetVideoCaptureFormat()); + base::ThreadTaskRunnerHandle::Get()); client_a_.reset(new MockVideoCaptureControllerEventHandler( controller_.get())); client_b_.reset(new MockVideoCaptureControllerEventHandler( @@ -132,15 +132,14 @@ void TearDown() override { base::RunLoop().RunUntilIdle(); } - scoped_refptr<media::VideoFrame> WrapI420Buffer( - const scoped_refptr<media::VideoCaptureDevice::Client::Buffer>& buffer, - gfx::Size dimensions) { + scoped_refptr<media::VideoFrame> WrapI420Buffer(gfx::Size dimensions, + uint8* data) { return media::VideoFrame::WrapExternalPackedMemory( media::VideoFrame::I420, dimensions, gfx::Rect(dimensions), dimensions, - reinterpret_cast<uint8*>(buffer->data()), + data, media::VideoFrame::AllocationSize(media::VideoFrame::I420, dimensions), base::SharedMemory::NULLHandle(), 0, @@ -325,9 +324,9 @@ // Now, simulate an incoming captured buffer from the capture device. As a // side effect this will cause the first buffer to be shared with clients. uint8 buffer_no = 1; - scoped_refptr<media::VideoCaptureDevice::Client::Buffer> buffer; - buffer = device_->ReserveOutputBuffer(media::PIXEL_FORMAT_I420, - capture_resolution); + scoped_ptr<media::VideoCaptureDevice::Client::Buffer> buffer( + device_->ReserveOutputBuffer(media::PIXEL_FORMAT_I420, + capture_resolution)); ASSERT_TRUE(buffer.get()); memset(buffer->data(), buffer_no++, buffer->size()); { @@ -345,11 +344,10 @@ EXPECT_CALL(*client_a_, DoBufferCreated(client_a_route_2)).Times(1); EXPECT_CALL(*client_a_, DoBufferReady(client_a_route_2,_)).Times(1); } - device_->OnIncomingCapturedVideoFrame( - buffer, - WrapI420Buffer(buffer, capture_resolution), - base::TimeTicks()); - buffer = NULL; + scoped_refptr<media::VideoFrame> video_frame = + WrapI420Buffer(capture_resolution, static_cast<uint8*>(buffer->data())); + device_->OnIncomingCapturedVideoFrame(buffer.Pass(), video_frame, + base::TimeTicks()); base::RunLoop().RunUntilIdle(); Mock::VerifyAndClearExpectations(client_a_.get()); @@ -358,15 +356,15 @@ // Second buffer which ought to use the same shared memory buffer. In this // case pretend that the Buffer pointer is held by the device for a long // delay. This shouldn't affect anything. - buffer = device_->ReserveOutputBuffer(media::PIXEL_FORMAT_I420, - capture_resolution); - ASSERT_TRUE(buffer.get()); - memset(buffer->data(), buffer_no++, buffer->size()); - device_->OnIncomingCapturedVideoFrame( - buffer, - WrapI420Buffer(buffer, capture_resolution), - base::TimeTicks()); - buffer = NULL; + scoped_ptr<media::VideoCaptureDevice::Client::Buffer> buffer2 = + device_->ReserveOutputBuffer(media::PIXEL_FORMAT_I420, + capture_resolution); + ASSERT_TRUE(buffer2.get()); + memset(buffer2->data(), buffer_no++, buffer2->size()); + video_frame = + WrapI420Buffer(capture_resolution, static_cast<uint8*>(buffer2->data())); + device_->OnIncomingCapturedVideoFrame(buffer2.Pass(), video_frame, + base::TimeTicks()); // The buffer should be delivered to the clients in any order. EXPECT_CALL(*client_a_, DoBufferReady(client_a_route_1,_)).Times(1); @@ -386,15 +384,15 @@ // Third, fourth, and fifth buffers. Pretend they all arrive at the same time. for (int i = 0; i < kPoolSize; i++) { - buffer = device_->ReserveOutputBuffer(media::PIXEL_FORMAT_I420, - capture_resolution); + scoped_ptr<media::VideoCaptureDevice::Client::Buffer> buffer = + device_->ReserveOutputBuffer(media::PIXEL_FORMAT_I420, + capture_resolution); ASSERT_TRUE(buffer.get()); memset(buffer->data(), buffer_no++, buffer->size()); - device_->OnIncomingCapturedVideoFrame( - buffer, - WrapI420Buffer(buffer, capture_resolution), - base::TimeTicks()); - buffer = NULL; + video_frame = + WrapI420Buffer(capture_resolution, static_cast<uint8*>(buffer->data())); + device_->OnIncomingCapturedVideoFrame(buffer.Pass(), video_frame, + base::TimeTicks()); } // ReserveOutputBuffer ought to fail now, because the pool is depleted. ASSERT_FALSE(device_->ReserveOutputBuffer(media::PIXEL_FORMAT_I420, @@ -423,30 +421,31 @@ EXPECT_CALL(*client_b_, DoEnded(client_b_route_1)).Times(1); controller_->StopSession(300); // Queue up another buffer. - buffer = device_->ReserveOutputBuffer(media::PIXEL_FORMAT_I420, - capture_resolution); - ASSERT_TRUE(buffer.get()); - memset(buffer->data(), buffer_no++, buffer->size()); - device_->OnIncomingCapturedVideoFrame( - buffer, - WrapI420Buffer(buffer, capture_resolution), - base::TimeTicks()); - buffer = NULL; - buffer = device_->ReserveOutputBuffer(media::PIXEL_FORMAT_I420, - capture_resolution); + scoped_ptr<media::VideoCaptureDevice::Client::Buffer> buffer3 = + device_->ReserveOutputBuffer(media::PIXEL_FORMAT_I420, + capture_resolution); + ASSERT_TRUE(buffer3.get()); + memset(buffer3->data(), buffer_no++, buffer3->size()); + video_frame = + WrapI420Buffer(capture_resolution, static_cast<uint8*>(buffer3->data())); + device_->OnIncomingCapturedVideoFrame(buffer3.Pass(), video_frame, + base::TimeTicks()); + + scoped_ptr<media::VideoCaptureDevice::Client::Buffer> buffer4 = + device_->ReserveOutputBuffer(media::PIXEL_FORMAT_I420, + capture_resolution); { // Kill A2 via session close (posts a task to disconnect, but A2 must not // be sent either of these two buffers). EXPECT_CALL(*client_a_, DoEnded(client_a_route_2)).Times(1); controller_->StopSession(200); } - ASSERT_TRUE(buffer.get()); - memset(buffer->data(), buffer_no++, buffer->size()); - device_->OnIncomingCapturedVideoFrame( - buffer, - WrapI420Buffer(buffer, capture_resolution), - base::TimeTicks()); - buffer = NULL; + ASSERT_TRUE(buffer4.get()); + memset(buffer4->data(), buffer_no++, buffer4->size()); + video_frame = + WrapI420Buffer(capture_resolution, static_cast<uint8*>(buffer4->data())); + device_->OnIncomingCapturedVideoFrame(buffer4.Pass(), video_frame, + base::TimeTicks()); // B2 is the only client left, and is the only one that should // get the buffer. EXPECT_CALL(*client_b_, DoBufferReady(client_b_route_2,_)).Times(2); @@ -469,32 +468,32 @@ } for (int i = 0; i < shm_buffers; ++i) { - buffer = device_->ReserveOutputBuffer(media::PIXEL_FORMAT_I420, - capture_resolution); + scoped_ptr<media::VideoCaptureDevice::Client::Buffer> buffer = + device_->ReserveOutputBuffer(media::PIXEL_FORMAT_I420, + capture_resolution); ASSERT_TRUE(buffer.get()); - device_->OnIncomingCapturedVideoFrame( - buffer, - WrapI420Buffer(buffer, capture_resolution), - base::TimeTicks()); - buffer = NULL; + video_frame = + WrapI420Buffer(capture_resolution, static_cast<uint8*>(buffer->data())); + device_->OnIncomingCapturedVideoFrame(buffer.Pass(), video_frame, + base::TimeTicks()); } std::vector<uint32> mailbox_syncpoints(mailbox_buffers); std::vector<uint32> release_syncpoints(mailbox_buffers); for (int i = 0; i < mailbox_buffers; ++i) { - buffer = device_->ReserveOutputBuffer(media::PIXEL_FORMAT_TEXTURE, - capture_resolution); + scoped_ptr<media::VideoCaptureDevice::Client::Buffer> buffer = + device_->ReserveOutputBuffer(media::PIXEL_FORMAT_TEXTURE, + capture_resolution); ASSERT_TRUE(buffer.get()); #if !defined(OS_ANDROID) mailbox_syncpoints[i] = gl_helper->InsertSyncPoint(); #endif device_->OnIncomingCapturedVideoFrame( - buffer, + buffer.Pass(), WrapMailboxBuffer(make_scoped_ptr(new gpu::MailboxHolder( gpu::Mailbox(), 0, mailbox_syncpoints[i])), base::Bind(&CacheSyncPoint, &release_syncpoints[i]), capture_resolution), base::TimeTicks()); - buffer = NULL; } // ReserveOutputBuffers ought to fail now regardless of buffer format, because // the pool is depleted. @@ -549,16 +548,14 @@ base::RunLoop().RunUntilIdle(); Mock::VerifyAndClearExpectations(client_b_.get()); - scoped_refptr<media::VideoCaptureDevice::Client::Buffer> buffer = + scoped_ptr<media::VideoCaptureDevice::Client::Buffer> buffer( device_->ReserveOutputBuffer(media::PIXEL_FORMAT_I420, - capture_resolution); + capture_resolution)); ASSERT_TRUE(buffer.get()); - - device_->OnIncomingCapturedVideoFrame( - buffer, - WrapI420Buffer(buffer, capture_resolution), - base::TimeTicks()); - buffer = NULL; + scoped_refptr<media::VideoFrame> video_frame = + WrapI420Buffer(capture_resolution, static_cast<uint8*>(buffer->data())); + device_->OnIncomingCapturedVideoFrame(buffer.Pass(), video_frame, + base::TimeTicks()); base::RunLoop().RunUntilIdle(); } @@ -587,16 +584,15 @@ Mock::VerifyAndClearExpectations(client_a_.get()); const gfx::Size dims(320, 240); - scoped_refptr<media::VideoCaptureDevice::Client::Buffer> buffer = - device_->ReserveOutputBuffer(media::PIXEL_FORMAT_I420, dims); + scoped_ptr<media::VideoCaptureDevice::Client::Buffer> buffer( + device_->ReserveOutputBuffer(media::PIXEL_FORMAT_I420, dims)); ASSERT_TRUE(buffer.get()); - device_->OnError("Test error"); - device_->OnIncomingCapturedVideoFrame( - buffer, - WrapI420Buffer(buffer, dims), - base::TimeTicks()); - buffer = NULL; + scoped_refptr<media::VideoFrame> video_frame = + WrapI420Buffer(dims, static_cast<uint8*>(buffer->data())); + device_->OnError("Test Error"); + device_->OnIncomingCapturedVideoFrame(buffer.Pass(), video_frame, + base::TimeTicks()); EXPECT_CALL(*client_a_, DoError(route_id)).Times(1); base::RunLoop().RunUntilIdle();
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 66b7fdb..7310963 100644 --- a/content/browser/renderer_host/media/video_capture_device_client.cc +++ b/content/browser/renderer_host/media/video_capture_device_client.cc
@@ -7,12 +7,23 @@ #include "base/bind.h" #include "base/strings/stringprintf.h" #include "base/trace_event/trace_event.h" +#include "content/browser/compositor/image_transport_factory.h" +#include "content/browser/gpu/browser_gpu_channel_host_factory.h" +#include "content/browser/gpu/browser_gpu_memory_buffer_manager.h" +#include "content/browser/gpu/gpu_data_manager_impl.h" #include "content/browser/renderer_host/media/video_capture_buffer_pool.h" #include "content/browser/renderer_host/media/video_capture_controller.h" +#include "content/common/gpu/client/context_provider_command_buffer.h" +#include "content/common/gpu/client/gl_helper.h" +#include "content/common/gpu/client/gpu_channel_host.h" +#include "content/common/gpu/client/webgraphicscontext3d_command_buffer_impl.h" +#include "content/common/gpu/gpu_process_launch_causes.h" #include "content/public/browser/browser_thread.h" +#include "gpu/command_buffer/common/mailbox_holder.h" #include "media/base/bind_to_current_loop.h" #include "media/base/video_capture_types.h" #include "media/base/video_frame.h" +#include "third_party/khronos/GLES2/gl2ext.h" #include "third_party/libyuv/include/libyuv.h" using media::VideoCaptureFormat; @@ -20,39 +31,173 @@ namespace content { +namespace { + +#if !defined(OS_ANDROID) +// Modelled after GpuProcessTransportFactory::CreateContextCommon(). +scoped_ptr<content::WebGraphicsContext3DCommandBufferImpl> CreateContextCommon( + scoped_refptr<content::GpuChannelHost> gpu_channel_host, + int surface_id) { + if (!content::GpuDataManagerImpl::GetInstance()-> + CanUseGpuBrowserCompositor()) { + DLOG(ERROR) << "No accelerated graphics found. Check chrome://gpu"; + return scoped_ptr<content::WebGraphicsContext3DCommandBufferImpl>(); + } + blink::WebGraphicsContext3D::Attributes attrs; + attrs.shareResources = true; + attrs.depth = false; + attrs.stencil = false; + attrs.antialias = false; + attrs.noAutomaticFlushes = true; + + if (!gpu_channel_host.get()) { + DLOG(ERROR) << "Failed to establish GPU channel."; + return scoped_ptr<content::WebGraphicsContext3DCommandBufferImpl>(); + } + GURL url("chrome://gpu/GpuProcessTransportFactory::CreateCaptureContext"); + return make_scoped_ptr( + new WebGraphicsContext3DCommandBufferImpl( + surface_id, + url, + gpu_channel_host.get(), + attrs, + true /* lose_context_when_out_of_memory */, + content::WebGraphicsContext3DCommandBufferImpl::SharedMemoryLimits(), + NULL)); +} + +// Modelled after +// GpuProcessTransportFactory::CreateOffscreenCommandBufferContext(). +scoped_ptr<content::WebGraphicsContext3DCommandBufferImpl> +CreateOffscreenCommandBufferContext() { + content::CauseForGpuLaunch cause = content::CAUSE_FOR_GPU_LAUNCH_CANVAS_2D; + // Android does not support synchronous opening of GPU channels. Should use + // EstablishGpuChannel() instead. + if (!content::BrowserGpuChannelHostFactory::instance()) + return scoped_ptr<content::WebGraphicsContext3DCommandBufferImpl>(); + scoped_refptr<content::GpuChannelHost> gpu_channel_host( + content::BrowserGpuChannelHostFactory::instance()-> + EstablishGpuChannelSync(cause)); + DCHECK(gpu_channel_host); + return CreateContextCommon(gpu_channel_host, 0); +} +#endif + +typedef base::Callback<void(scoped_refptr<ContextProviderCommandBuffer>)> + ProcessContextCallback; + +void CreateContextOnUIThread(ProcessContextCallback bottom_half) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); +#if !defined(OS_ANDROID) + bottom_half.Run(ContextProviderCommandBuffer::Create( + CreateOffscreenCommandBufferContext(), "Offscreen-CaptureThread")); + return; +#endif +} + +void ResetLostContextCallback( + const scoped_refptr<ContextProviderCommandBuffer>& capture_thread_context) { + capture_thread_context->SetLostContextCallback( + cc::ContextProvider::LostContextCallback()); +} + +} // anonymous namespace + // Class combining a Client::Buffer interface implementation and a pool buffer // implementation to guarantee proper cleanup on destruction on our side. class AutoReleaseBuffer : public media::VideoCaptureDevice::Client::Buffer { public: AutoReleaseBuffer(const scoped_refptr<VideoCaptureBufferPool>& pool, - int buffer_id, - void* data, - size_t size) - : pool_(pool), - id_(buffer_id), - data_(data), - size_(size) { + int buffer_id) + : id_(buffer_id), + pool_(pool), + buffer_handle_(pool_->GetBufferHandle(buffer_id).Pass()) { DCHECK(pool_.get()); } int id() const override { return id_; } - void* data() const override { return data_; } - size_t size() const override { return size_; } + size_t size() const override { return buffer_handle_->size(); } + void* data() override { return buffer_handle_->data(); } + ClientBuffer AsClientBuffer() override { + return buffer_handle_->AsClientBuffer(); + } private: ~AutoReleaseBuffer() override { pool_->RelinquishProducerReservation(id_); } - const scoped_refptr<VideoCaptureBufferPool> pool_; const int id_; - void* const data_; - const size_t size_; + const scoped_refptr<VideoCaptureBufferPool> pool_; + const scoped_ptr<VideoCaptureBufferPool::BufferHandle> buffer_handle_; +}; + +// Internal ref-counted class wrapping an incoming GpuMemoryBuffer into a +// Texture backed VideoFrame. This VideoFrame creation is balanced by a waiting +// on the associated |sync_point|. After VideoFrame consumption the inserted +// ReleaseCallback() will be called, where the Texture is destroyed. +// +// This class jumps between threads due to GPU-related thread limitations, i.e. +// some objects cannot be accessed from IO Thread whereas others need to be +// constructed on UI Thread. For this reason most of the operations are carried +// out on Capture Thread (|capture_task_runner_|). +class VideoCaptureDeviceClient::TextureWrapHelper final + : public base::RefCountedThreadSafe<TextureWrapHelper> { + public: + TextureWrapHelper( + const base::WeakPtr<VideoCaptureController>& controller, + const scoped_refptr<base::SingleThreadTaskRunner>& capture_task_runner); + + // Wraps the GpuMemoryBuffer-backed |buffer| into a Texture, and sends it to + // |controller_| wrapped in a VideoFrame. + void OnIncomingCapturedGpuMemoryBuffer( + scoped_ptr<media::VideoCaptureDevice::Client::Buffer> buffer, + const media::VideoCaptureFormat& frame_format, + const base::TimeTicks& timestamp); + + private: + friend class base::RefCountedThreadSafe<TextureWrapHelper>; + ~TextureWrapHelper(); + + // Creates some necessary members in |capture_task_runner_|. + void Init(); + // Runs the bottom half of the GlHelper creation. + void CreateGlHelper( + scoped_refptr<ContextProviderCommandBuffer> capture_thread_context); + + // Recycles |memory_buffer|, deletes Image and Texture on VideoFrame release. + void ReleaseCallback(GLuint image_id, + GLuint texture_id, + uint32 sync_point); + + // The Command Buffer lost the GL context, f.i. GPU process crashed. Signal + // error to our owner so the capture can be torn down. + void LostContextCallback(); + + // Prints the error |message| and notifies |controller_| of an error. + void OnError(const std::string& message); + + // |controller_| should only be used on IO thread. + const base::WeakPtr<VideoCaptureController> controller_; + const scoped_refptr<base::SingleThreadTaskRunner> capture_task_runner_; + + // Command buffer reference, needs to be destroyed when unused. It is created + // on UI Thread and bound to Capture Thread. In particular, it cannot be used + // from IO Thread. + scoped_refptr<ContextProviderCommandBuffer> capture_thread_context_; + // Created and used from Capture Thread. Cannot be used from IO Thread. + scoped_ptr<GLHelper> gl_helper_; + + DISALLOW_COPY_AND_ASSIGN(TextureWrapHelper); }; VideoCaptureDeviceClient::VideoCaptureDeviceClient( const base::WeakPtr<VideoCaptureController>& controller, - const scoped_refptr<VideoCaptureBufferPool>& buffer_pool) + const scoped_refptr<VideoCaptureBufferPool>& buffer_pool, + const scoped_refptr<base::SingleThreadTaskRunner>& capture_task_runner) : controller_(controller), buffer_pool_(buffer_pool), - last_captured_pixel_format_(media::PIXEL_FORMAT_UNKNOWN) {} + capture_task_runner_(capture_task_runner), + last_captured_pixel_format_(media::PIXEL_FORMAT_UNKNOWN) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); +} VideoCaptureDeviceClient::~VideoCaptureDeviceClient() {} @@ -105,8 +250,8 @@ return; } - scoped_refptr<Buffer> buffer = - ReserveOutputBuffer(media::PIXEL_FORMAT_I420, dimensions); + scoped_ptr<Buffer> buffer( + ReserveOutputBuffer(media::PIXEL_FORMAT_I420, dimensions)); if (!buffer.get()) return; @@ -201,31 +346,12 @@ frame_format.pixel_format); return; } - scoped_refptr<VideoFrame> frame = - VideoFrame::WrapExternalPackedMemory( - VideoFrame::I420, - dimensions, - gfx::Rect(dimensions), - dimensions, - yplane, - VideoFrame::AllocationSize(VideoFrame::I420, dimensions), - base::SharedMemory::NULLHandle(), - 0, - base::TimeDelta(), - base::Closure()); - DCHECK(frame.get()); - frame->metadata()->SetDouble(media::VideoFrameMetadata::FRAME_RATE, - frame_format.frame_rate); - BrowserThread::PostTask( - BrowserThread::IO, - FROM_HERE, - base::Bind( - &VideoCaptureController::DoIncomingCapturedVideoFrameOnIOThread, - controller_, - buffer, - frame, - timestamp)); + OnIncomingCapturedBuffer(buffer.Pass(), + media::VideoCaptureFormat(dimensions, + frame_format.frame_rate, + media::PIXEL_FORMAT_I420), + timestamp); } void @@ -243,8 +369,8 @@ DCHECK_EQ(frame_format.pixel_format, media::PIXEL_FORMAT_I420); DCHECK_EQ(clockwise_rotation, 0) << "Rotation not supported"; - scoped_refptr<Buffer> buffer = - ReserveOutputBuffer(frame_format.pixel_format, frame_format.frame_size); + scoped_ptr<Buffer> buffer( + ReserveOutputBuffer(frame_format.pixel_format, frame_format.frame_size)); if (!buffer.get()) return; @@ -280,46 +406,31 @@ return; } - scoped_refptr<VideoFrame> video_frame = VideoFrame::WrapExternalYuvData( - VideoFrame::I420, frame_format.frame_size, - gfx::Rect(frame_format.frame_size), frame_format.frame_size, y_stride, - u_stride, v_stride, dst_y, dst_u, dst_v, base::TimeDelta(), - base::Closure()); - DCHECK(video_frame.get()); - video_frame->metadata()->SetDouble(media::VideoFrameMetadata::FRAME_RATE, - frame_format.frame_rate); - - BrowserThread::PostTask( - BrowserThread::IO, - FROM_HERE, - base::Bind( - &VideoCaptureController::DoIncomingCapturedVideoFrameOnIOThread, - controller_, - buffer, - video_frame, - timestamp)); + OnIncomingCapturedBuffer(buffer.Pass(), frame_format, timestamp); }; -scoped_refptr<media::VideoCaptureDevice::Client::Buffer> +scoped_ptr<media::VideoCaptureDevice::Client::Buffer> VideoCaptureDeviceClient::ReserveOutputBuffer(media::VideoPixelFormat format, const gfx::Size& dimensions) { - DCHECK(format == media::PIXEL_FORMAT_TEXTURE || - format == media::PIXEL_FORMAT_I420 || - format == media::PIXEL_FORMAT_ARGB); + DCHECK(format == media::PIXEL_FORMAT_I420 || + format == media::PIXEL_FORMAT_TEXTURE || + format == media::PIXEL_FORMAT_GPUMEMORYBUFFER); DCHECK_GT(dimensions.width(), 0); DCHECK_GT(dimensions.height(), 0); + if (format == media::PIXEL_FORMAT_GPUMEMORYBUFFER && !texture_wrap_helper_) { + texture_wrap_helper_ = + new TextureWrapHelper(controller_, capture_task_runner_); + } + int buffer_id_to_drop = VideoCaptureBufferPool::kInvalidId; const int buffer_id = buffer_pool_->ReserveForProducer(format, dimensions, &buffer_id_to_drop); if (buffer_id == VideoCaptureBufferPool::kInvalidId) return NULL; - void* data; - size_t size; - buffer_pool_->GetBufferInfo(buffer_id, &data, &size); - scoped_refptr<media::VideoCaptureDevice::Client::Buffer> output_buffer( - new AutoReleaseBuffer(buffer_pool_, buffer_id, data, size)); + scoped_ptr<media::VideoCaptureDevice::Client::Buffer> output_buffer( + new AutoReleaseBuffer(buffer_pool_, buffer_id)); if (buffer_id_to_drop != VideoCaptureBufferPool::kInvalidId) { BrowserThread::PostTask(BrowserThread::IO, @@ -328,12 +439,45 @@ controller_, buffer_id_to_drop)); } - return output_buffer; + return output_buffer.Pass(); } -void -VideoCaptureDeviceClient::OnIncomingCapturedVideoFrame( - const scoped_refptr<Buffer>& buffer, +void VideoCaptureDeviceClient::OnIncomingCapturedBuffer( + scoped_ptr<Buffer> buffer, + const media::VideoCaptureFormat& frame_format, + const base::TimeTicks& timestamp) { + if (frame_format.pixel_format == media::PIXEL_FORMAT_GPUMEMORYBUFFER) { + capture_task_runner_->PostTask( + FROM_HERE, + base::Bind(&TextureWrapHelper::OnIncomingCapturedGpuMemoryBuffer, + texture_wrap_helper_, + base::Passed(&buffer), + frame_format, + timestamp)); + } else { + DCHECK_EQ(frame_format.pixel_format, media::PIXEL_FORMAT_I420); + scoped_refptr<VideoFrame> video_frame = + VideoFrame::WrapExternalPackedMemory( + VideoFrame::I420, + frame_format.frame_size, + gfx::Rect(frame_format.frame_size), + frame_format.frame_size, + reinterpret_cast<uint8*>(buffer->data()), + VideoFrame::AllocationSize(VideoFrame::I420, + frame_format.frame_size), + base::SharedMemory::NULLHandle(), + 0 /* shared_memory_offset */, + base::TimeDelta(), + base::Closure()); + DCHECK(video_frame.get()); + video_frame->metadata()->SetDouble(media::VideoFrameMetadata::FRAME_RATE, + frame_format.frame_rate); + OnIncomingCapturedVideoFrame(buffer.Pass(), video_frame, timestamp); + } +} + +void VideoCaptureDeviceClient::OnIncomingCapturedVideoFrame( + scoped_ptr<Buffer> buffer, const scoped_refptr<VideoFrame>& frame, const base::TimeTicks& timestamp) { BrowserThread::PostTask( @@ -342,7 +486,7 @@ base::Bind( &VideoCaptureController::DoIncomingCapturedVideoFrameOnIOThread, controller_, - buffer, + base::Passed(&buffer), frame, timestamp)); } @@ -368,4 +512,162 @@ controller_, message)); } +VideoCaptureDeviceClient::TextureWrapHelper::TextureWrapHelper( + const base::WeakPtr<VideoCaptureController>& controller, + const scoped_refptr<base::SingleThreadTaskRunner>& capture_task_runner) + : controller_(controller), + capture_task_runner_(capture_task_runner) { + capture_task_runner_->PostTask(FROM_HERE, + base::Bind(&TextureWrapHelper::Init, this)); +} + +void +VideoCaptureDeviceClient::TextureWrapHelper::OnIncomingCapturedGpuMemoryBuffer( + scoped_ptr<media::VideoCaptureDevice::Client::Buffer> buffer, + const media::VideoCaptureFormat& frame_format, + const base::TimeTicks& timestamp) { + DCHECK(capture_task_runner_->BelongsToCurrentThread()); + DCHECK_EQ(frame_format.pixel_format, media::PIXEL_FORMAT_GPUMEMORYBUFFER); + if (!gl_helper_) { + // |gl_helper_| might not exist due to asynchronous initialization not + // finished or due to termination in process after a context loss. + DVLOG(1) << " Skipping ingress frame, no GL context."; + return; + } + + gpu::gles2::GLES2Interface* gl = capture_thread_context_->ContextGL(); + GLuint image_id = gl->CreateImageCHROMIUM(buffer->AsClientBuffer(), + frame_format.frame_size.width(), + frame_format.frame_size.height(), + GL_BGRA_EXT); + DCHECK(image_id); + + GLuint texture_id = gl_helper_->CreateTexture(); + DCHECK(texture_id); + { + content::ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(gl, texture_id); + gl->BindTexImage2DCHROMIUM(GL_TEXTURE_2D, image_id); + } + + scoped_ptr<gpu::MailboxHolder> mailbox_holder(new gpu::MailboxHolder( + gl_helper_->ProduceMailboxHolderFromTexture(texture_id))); + DCHECK(!mailbox_holder->mailbox.IsZero()); + DCHECK(mailbox_holder->mailbox.Verify()); + DCHECK(mailbox_holder->texture_target); + DCHECK(mailbox_holder->sync_point); + + scoped_refptr<media::VideoFrame> video_frame = + media::VideoFrame::WrapNativeTexture( + mailbox_holder.Pass(), + media::BindToCurrentLoop( + base::Bind(&VideoCaptureDeviceClient::TextureWrapHelper:: + ReleaseCallback, + this, image_id, texture_id)), + frame_format.frame_size, + gfx::Rect(frame_format.frame_size), + frame_format.frame_size, + base::TimeDelta(), + true /* allow_overlay */); + video_frame->metadata()->SetDouble(media::VideoFrameMetadata::FRAME_RATE, + frame_format.frame_rate); + + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + base::Bind( + &VideoCaptureController::DoIncomingCapturedVideoFrameOnIOThread, + controller_, base::Passed(&buffer), video_frame, timestamp)); +} + +VideoCaptureDeviceClient::TextureWrapHelper::~TextureWrapHelper() { + // Might not be running on capture_task_runner_'s thread. Ensure owned objects + // are destroyed on the correct threads. + if (gl_helper_) + capture_task_runner_->DeleteSoon(FROM_HERE, gl_helper_.release()); + + if (capture_thread_context_) { + capture_task_runner_->PostTask( + FROM_HERE, + base::Bind(&ResetLostContextCallback, capture_thread_context_)); + capture_thread_context_->AddRef(); + ContextProviderCommandBuffer* raw_capture_thread_context = + capture_thread_context_.get(); + capture_thread_context_ = nullptr; + capture_task_runner_->ReleaseSoon(FROM_HERE, raw_capture_thread_context); + } +} + +void VideoCaptureDeviceClient::TextureWrapHelper::Init() { + DCHECK(capture_task_runner_->BelongsToCurrentThread()); + + // In threaded compositing mode, we have to create our own context for Capture + // to avoid using the GPU command queue from multiple threads. Context + // creation must happen on UI thread; then the context needs to be bound to + // the appropriate thread, which is done in CreateGlHelper(). + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + base::Bind( + &CreateContextOnUIThread, + media::BindToCurrentLoop(base::Bind( + &VideoCaptureDeviceClient::TextureWrapHelper::CreateGlHelper, + this)))); +} + +void VideoCaptureDeviceClient::TextureWrapHelper::CreateGlHelper( + scoped_refptr<ContextProviderCommandBuffer> capture_thread_context) { + DCHECK(capture_task_runner_->BelongsToCurrentThread()); + + if (!capture_thread_context.get()) { + DLOG(ERROR) << "No offscreen GL Context!"; + return; + } + // This may not happen in IO Thread. The destructor resets the context lost + // callback, so base::Unretained is safe; otherwise it'd be a circular ref + // counted dependency. + capture_thread_context->SetLostContextCallback(media::BindToCurrentLoop( + base::Bind( + &VideoCaptureDeviceClient::TextureWrapHelper::LostContextCallback, + base::Unretained(this)))); + if (!capture_thread_context->BindToCurrentThread()) { + capture_thread_context = NULL; + DLOG(ERROR) << "Couldn't bind the Capture Context to the Capture Thread."; + return; + } + DCHECK(capture_thread_context); + capture_thread_context_ = capture_thread_context; + + // At this point, |capture_thread_context| is a cc::ContextProvider. Creation + // of our GLHelper should happen on Capture Thread. + gl_helper_.reset(new GLHelper(capture_thread_context->ContextGL(), + capture_thread_context->ContextSupport())); + DCHECK(gl_helper_); +} + +void VideoCaptureDeviceClient::TextureWrapHelper::ReleaseCallback( + GLuint image_id, + GLuint texture_id, + uint32 sync_point) { + DCHECK(capture_task_runner_->BelongsToCurrentThread()); + + if (gl_helper_) { + gl_helper_->DeleteTexture(texture_id); + capture_thread_context_->ContextGL()->DestroyImageCHROMIUM(image_id); + } +} + +void VideoCaptureDeviceClient::TextureWrapHelper::LostContextCallback() { + DCHECK(capture_task_runner_->BelongsToCurrentThread()); + // Prevent incoming frames from being processed while OnError gets groked. + gl_helper_.reset(); + OnError("GLContext lost"); +} + +void VideoCaptureDeviceClient::TextureWrapHelper::OnError( + const std::string& message) { + DCHECK(capture_task_runner_->BelongsToCurrentThread()); + DLOG(ERROR) << message; + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + base::Bind(&VideoCaptureController::DoErrorOnIOThread, controller_)); +} + } // namespace content
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 7da95da..afb6954 100644 --- a/content/browser/renderer_host/media/video_capture_device_client.h +++ b/content/browser/renderer_host/media/video_capture_device_client.h
@@ -24,12 +24,18 @@ // often be called on some auxiliary thread depending on the platform and the // device type; including, for example, the DirectShow thread on Windows, the // v4l2_thread on Linux, and the UI thread for tab capture. +// +// It has an internal ref counted TextureWrapHelper class used to wrap incoming +// GpuMemoryBuffers into Texture backed VideoFrames. This class creates and +// manages the necessary entities to interact with the GPU process, notably an +// offscreen Context to avoid janking the UI thread. class CONTENT_EXPORT VideoCaptureDeviceClient : public media::VideoCaptureDevice::Client { public: VideoCaptureDeviceClient( const base::WeakPtr<VideoCaptureController>& controller, - const scoped_refptr<VideoCaptureBufferPool>& buffer_pool); + const scoped_refptr<VideoCaptureBufferPool>& buffer_pool, + const scoped_refptr<base::SingleThreadTaskRunner>& capture_task_runner); ~VideoCaptureDeviceClient() override; // VideoCaptureDevice::Client implementation. @@ -47,11 +53,13 @@ const media::VideoCaptureFormat& frame_format, int clockwise_rotation, const base::TimeTicks& timestamp) override; - scoped_refptr<Buffer> ReserveOutputBuffer( - media::VideoPixelFormat format, - const gfx::Size& dimensions) override; + scoped_ptr<Buffer> ReserveOutputBuffer(media::VideoPixelFormat format, + const gfx::Size& dimensions) override; + void OnIncomingCapturedBuffer(scoped_ptr<Buffer> buffer, + const media::VideoCaptureFormat& frame_format, + const base::TimeTicks& timestamp) override; void OnIncomingCapturedVideoFrame( - const scoped_refptr<Buffer>& buffer, + scoped_ptr<Buffer> buffer, const scoped_refptr<media::VideoFrame>& frame, const base::TimeTicks& timestamp) override; void OnError(const std::string& reason) override; @@ -64,6 +72,13 @@ // The pool of shared-memory buffers used for capturing. const scoped_refptr<VideoCaptureBufferPool> buffer_pool_; + // Internal delegate for GpuMemoryBuffer-into-VideoFrame wrapping. + class TextureWrapHelper; + scoped_refptr<TextureWrapHelper> texture_wrap_helper_; + // Reference to Capture Thread task runner, where |texture_wrap_helper_| + // lives. + const scoped_refptr<base::SingleThreadTaskRunner> capture_task_runner_; + media::VideoPixelFormat last_captured_pixel_format_; DISALLOW_COPY_AND_ASSIGN(VideoCaptureDeviceClient);
diff --git a/content/browser/renderer_host/media/video_capture_manager.cc b/content/browser/renderer_host/media/video_capture_manager.cc index f1c14ebc..a213a564 100644 --- a/content/browser/renderer_host/media/video_capture_manager.cc +++ b/content/browser/renderer_host/media/video_capture_manager.cc
@@ -332,7 +332,7 @@ entry->stream_type, request->params(), base::Passed(entry->video_capture_controller()->NewDeviceClient( - device_task_runner_, request->params().requested_format))), + device_task_runner_))), base::Bind(&VideoCaptureManager::OnDeviceStarted, this, request->serial_id())); }
diff --git a/content/browser/renderer_host/media/video_capture_texture_wrapper.cc b/content/browser/renderer_host/media/video_capture_texture_wrapper.cc deleted file mode 100644 index 339596e..0000000 --- a/content/browser/renderer_host/media/video_capture_texture_wrapper.cc +++ /dev/null
@@ -1,463 +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 "content/browser/renderer_host/media/video_capture_texture_wrapper.h" - -#include "base/bind.h" -#include "content/browser/compositor/image_transport_factory.h" -#include "content/browser/gpu/browser_gpu_channel_host_factory.h" -#include "content/browser/gpu/browser_gpu_memory_buffer_manager.h" -#include "content/browser/gpu/gpu_data_manager_impl.h" -#include "content/browser/renderer_host/media/video_capture_controller.h" -#include "content/common/gpu/client/context_provider_command_buffer.h" -#include "content/common/gpu/client/gl_helper.h" -#include "content/common/gpu/client/gpu_channel_host.h" -#include "content/common/gpu/client/webgraphicscontext3d_command_buffer_impl.h" -#include "content/common/gpu/gpu_process_launch_causes.h" -#include "content/public/browser/browser_thread.h" -#include "gpu/command_buffer/common/mailbox_holder.h" -#include "media/base/bind_to_current_loop.h" -#include "media/base/video_capture_types.h" -#include "media/base/video_frame.h" -#include "third_party/khronos/GLES2/gl2ext.h" -#include "third_party/libyuv/include/libyuv.h" - -namespace content { - -namespace { - -// VideoCaptureController has at most 3 capture frames in flight. -const size_t kNumGpuMemoryBuffers = 3; - -uint32 VideoPixelFormatToFourCC(media::VideoPixelFormat pixel_format) { - switch (pixel_format) { - // I420 is needed by Fake and FileVideoCaptureDevice - case media::PIXEL_FORMAT_I420: - return libyuv::FOURCC_I420; - case media::PIXEL_FORMAT_UYVY: - return libyuv::FOURCC_UYVY; - case media::PIXEL_FORMAT_YUY2: - return libyuv::FOURCC_YUY2; - case media::PIXEL_FORMAT_MJPEG: - return libyuv::FOURCC_MJPG; - default: - NOTREACHED() << "Bad captured pixel format: " - << media::VideoCaptureFormat::PixelFormatToString(pixel_format); - } - return libyuv::FOURCC_ANY; -} - -// Modelled after GpuProcessTransportFactory::CreateContextCommon(). -scoped_ptr<content::WebGraphicsContext3DCommandBufferImpl> CreateContextCommon( - scoped_refptr<content::GpuChannelHost> gpu_channel_host, - int surface_id) { - if (!content::GpuDataManagerImpl::GetInstance()-> - CanUseGpuBrowserCompositor()) { - DLOG(ERROR) << "No accelerated graphics found. Check chrome://gpu"; - return scoped_ptr<content::WebGraphicsContext3DCommandBufferImpl>(); - } - blink::WebGraphicsContext3D::Attributes attrs; - attrs.shareResources = true; - attrs.depth = false; - attrs.stencil = false; - attrs.antialias = false; - attrs.noAutomaticFlushes = true; - - if (!gpu_channel_host.get()) { - DLOG(ERROR) << "Failed to establish GPU channel."; - return scoped_ptr<content::WebGraphicsContext3DCommandBufferImpl>(); - } - GURL url("chrome://gpu/GpuProcessTransportFactory::CreateCaptureContext"); - return make_scoped_ptr( - new WebGraphicsContext3DCommandBufferImpl( - surface_id, - url, - gpu_channel_host.get(), - attrs, - true /* lose_context_when_out_of_memory */, - content::WebGraphicsContext3DCommandBufferImpl::SharedMemoryLimits(), - NULL)); -} - -// Modelled after -// GpuProcessTransportFactory::CreateOffscreenCommandBufferContext(). -scoped_ptr<content::WebGraphicsContext3DCommandBufferImpl> -CreateOffscreenCommandBufferContext() { - content::CauseForGpuLaunch cause = content::CAUSE_FOR_GPU_LAUNCH_CANVAS_2D; - scoped_refptr<content::GpuChannelHost> gpu_channel_host( - content::BrowserGpuChannelHostFactory::instance()-> - EstablishGpuChannelSync(cause)); - DCHECK(gpu_channel_host); - return CreateContextCommon(gpu_channel_host, 0); -} - -typedef base::Callback<void(scoped_refptr<ContextProviderCommandBuffer>)> - ProcessContextCallback; - -void CreateContextOnUIThread(ProcessContextCallback bottom_half) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - bottom_half.Run(ContextProviderCommandBuffer::Create( - CreateOffscreenCommandBufferContext(), "Offscreen-CaptureThread")); - return; -} - -void ResetLostContextCallback( - const scoped_refptr<ContextProviderCommandBuffer>& capture_thread_context) { - capture_thread_context->SetLostContextCallback( - cc::ContextProvider::LostContextCallback()); -} - -} // anonymous namespace - -// Internal ref-counted class to manage a pool of GpuMemoryBuffers. The contents -// of an incoming captured frame are_copied_ into the first available buffer -// from the pool and sent to our client ultimately wrapped into a VideoFrame. -// This VideoFrame creation is balanced by a waiting on the associated -// |sync_point|. After VideoFrame consumption the inserted ReleaseCallback() -// will be called, where the GpuMemoryBuffer is recycled. -// -// This class jumps between threads due to GPU-related thread limitations, i.e. -// some objects cannot be accessed from IO Thread, where we are constructed, -// others need to be constructed on UI Thread. For this reason most of the -// operations are carried out on Capture Thread (|capture_task_runner_|). -// -// TODO(mcasas): ctor |frame_format| is used for early GpuMemoryBuffer pool -// allocation, but VideoCaptureDevices might provide a different resolution when -// calling OnIncomingCapturedData(), be that due to driver preferences or to -// its ResolutionChangePolicy. Make the GpuMemoryBuffer allocated on demand. -class VideoCaptureTextureWrapper::TextureWrapperDelegate final - : public base::RefCountedThreadSafe<TextureWrapperDelegate> { - public: - TextureWrapperDelegate( - const base::WeakPtr<VideoCaptureController>& controller, - const scoped_refptr<base::SingleThreadTaskRunner>& capture_task_runner, - const media::VideoCaptureFormat& capture_format); - - // Copy-converts the incoming data into a GpuMemoruBuffer backed Texture, and - // sends it to |controller_| wrapped in a VideoFrame, with |buffer| as storage - // backend. - void OnIncomingCapturedData( - const scoped_refptr<media::VideoCaptureDevice::Client::Buffer>& - texture_buffer, - const scoped_refptr<media::VideoCaptureDevice::Client::Buffer>& - argb_buffer, - const gfx::Size& frame_size, - const base::TimeTicks& timestamp); - - private: - friend class base::RefCountedThreadSafe<TextureWrapperDelegate>; - ~TextureWrapperDelegate(); - - // Creates some necessary members in |capture_task_runner_|. - void Init(const media::VideoCaptureFormat& capture_format); - // Runs the bottom half of the GlHelper creation. - void CreateGlHelper( - scoped_refptr<ContextProviderCommandBuffer> capture_thread_context); - - // Recycles |memory_buffer|, deletes Image and Texture on VideoFrame release. - void ReleaseCallback(GLuint image_id, - GLuint texture_id, - linked_ptr<gfx::GpuMemoryBuffer> memory_buffer, - uint32 sync_point); - - // The Command Buffer lost the GL context, f.i. GPU process crashed. Signal - // error to our owner so the capture can be torn down. - void LostContextCallback(); - - // Prints the error |message| and notifies |controller_| of an error. - void OnError(const std::string& message); - - const base::WeakPtr<VideoCaptureController> controller_; - const scoped_refptr<base::SingleThreadTaskRunner> capture_task_runner_; - - // Command buffer reference, needs to be destroyed when unused. It is created - // on UI Thread and bound to Capture Thread. In particular, it cannot be used - // from IO Thread. - scoped_refptr<ContextProviderCommandBuffer> capture_thread_context_; - // Created and used from Capture Thread. Cannot be used from IO Thread. - scoped_ptr<GLHelper> gl_helper_; - - // A pool of GpuMemoryBuffers that are used to wrap incoming captured frames; - // recycled via ReleaseCallback(). - std::queue<linked_ptr<gfx::GpuMemoryBuffer>> gpu_memory_buffers_; - - DISALLOW_COPY_AND_ASSIGN(TextureWrapperDelegate); -}; - -VideoCaptureTextureWrapper::VideoCaptureTextureWrapper( - const base::WeakPtr<VideoCaptureController>& controller, - const scoped_refptr<VideoCaptureBufferPool>& buffer_pool, - const scoped_refptr<base::SingleThreadTaskRunner>& capture_task_runner, - const media::VideoCaptureFormat& capture_format) - : VideoCaptureDeviceClient(controller, buffer_pool), - wrapper_delegate_(new TextureWrapperDelegate(controller, - capture_task_runner, - capture_format)), - capture_task_runner_(capture_task_runner) { - DCHECK_CURRENTLY_ON(BrowserThread::IO); -} - -VideoCaptureTextureWrapper::~VideoCaptureTextureWrapper() { -} - -void VideoCaptureTextureWrapper::OnIncomingCapturedData( - const uint8* data, - int length, - const media::VideoCaptureFormat& frame_format, - int clockwise_rotation, - const base::TimeTicks& timestamp) { - - // Reserve a temporary Buffer for conversion to ARGB. - scoped_refptr<media::VideoCaptureDevice::Client::Buffer> argb_buffer = - ReserveOutputBuffer(media::PIXEL_FORMAT_ARGB, frame_format.frame_size); - DVLOG_IF(1, !argb_buffer) << "Couldn't allocate ARGB Buffer"; - if (!argb_buffer) - return; - - DCHECK(argb_buffer->data()); - // TODO(mcasas): Take |rotation| into acount. - int ret = libyuv::ConvertToARGB(data, - length, - reinterpret_cast<uint8*>(argb_buffer->data()), - frame_format.frame_size.width() * 4, - 0, - 0, - frame_format.frame_size.width(), - frame_format.frame_size.height(), - frame_format.frame_size.width(), - frame_format.frame_size.height(), - libyuv::kRotate0, - VideoPixelFormatToFourCC( - frame_format.pixel_format)); - DLOG_IF(ERROR, ret != 0) << "Error converting incoming frame"; - if (ret != 0) - return; - - // Reserve output buffer for the texture on the IPC borderlands. - scoped_refptr<media::VideoCaptureDevice::Client::Buffer> texture_buffer = - ReserveOutputBuffer(media::PIXEL_FORMAT_TEXTURE, frame_format.frame_size); - DVLOG_IF(1, !texture_buffer) << "Couldn't allocate Texture Buffer"; - if (!texture_buffer) - return; - - capture_task_runner_->PostTask( - FROM_HERE, - base::Bind( - &TextureWrapperDelegate::OnIncomingCapturedData, - wrapper_delegate_, - texture_buffer, - argb_buffer, - frame_format.frame_size, - timestamp)); -} - -VideoCaptureTextureWrapper::TextureWrapperDelegate::TextureWrapperDelegate( - const base::WeakPtr<VideoCaptureController>& controller, - const scoped_refptr<base::SingleThreadTaskRunner>& capture_task_runner, - const media::VideoCaptureFormat& capture_format) - : controller_(controller), - capture_task_runner_(capture_task_runner) { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - capture_task_runner_->PostTask(FROM_HERE, - base::Bind(&TextureWrapperDelegate::Init, this, capture_format)); -} - -void VideoCaptureTextureWrapper::TextureWrapperDelegate::OnIncomingCapturedData( - const scoped_refptr<media::VideoCaptureDevice::Client::Buffer>& - texture_buffer, - const scoped_refptr<media::VideoCaptureDevice::Client::Buffer>& argb_buffer, - const gfx::Size& frame_size, - const base::TimeTicks& timestamp) { - DCHECK(capture_task_runner_->BelongsToCurrentThread()); - - DVLOG_IF(1, !gl_helper_) << " Skipping ingress frame, no GL context."; - if (!gl_helper_) - return; - - DVLOG_IF(1, gpu_memory_buffers_.empty()) << " Skipping ingress frame, 0 GMBs"; - if (gpu_memory_buffers_.empty()) - return; - - linked_ptr<gfx::GpuMemoryBuffer> gpu_memory_buffer = - gpu_memory_buffers_.front(); - gpu_memory_buffers_.pop(); - DCHECK(gpu_memory_buffer.get()); - - void* data = NULL; - bool rv = gpu_memory_buffer->Map(&data); - DCHECK(rv); - int stride; - gpu_memory_buffer->GetStride(&stride); - - uint8* mapped_buffer = static_cast<uint8*>(data); - DCHECK(mapped_buffer); - libyuv::ARGBCopy( - reinterpret_cast<uint8*>(argb_buffer->data()), frame_size.width() * 4, - mapped_buffer, stride, - frame_size.width(), frame_size.height()); - gpu_memory_buffer->Unmap(); - - gpu::gles2::GLES2Interface* gl = capture_thread_context_->ContextGL(); - GLuint image_id = gl->CreateImageCHROMIUM(gpu_memory_buffer->AsClientBuffer(), - frame_size.width(), - frame_size.height(), GL_BGRA_EXT); - DCHECK(image_id); - - GLuint texture_id = gl_helper_->CreateTexture(); - DCHECK(texture_id); - { - content::ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(gl, texture_id); - gl->BindTexImage2DCHROMIUM(GL_TEXTURE_2D, image_id); - } - - scoped_ptr<gpu::MailboxHolder> mailbox_holder(new gpu::MailboxHolder( - gl_helper_->ProduceMailboxHolderFromTexture(texture_id))); - DCHECK(!mailbox_holder->mailbox.IsZero()); - DCHECK(mailbox_holder->mailbox.Verify()); - DCHECK(mailbox_holder->texture_target); - DCHECK(mailbox_holder->sync_point); - - scoped_refptr<media::VideoFrame> video_frame = - media::VideoFrame::WrapNativeTexture( - mailbox_holder.Pass(), - media::BindToCurrentLoop( - base::Bind(&VideoCaptureTextureWrapper::TextureWrapperDelegate:: - ReleaseCallback, - this, image_id, texture_id, gpu_memory_buffer)), - frame_size, - gfx::Rect(frame_size), - frame_size, - base::TimeDelta(), - true /* allow_overlay */); - - BrowserThread::PostTask( - BrowserThread::IO, FROM_HERE, - base::Bind( - &VideoCaptureController::DoIncomingCapturedVideoFrameOnIOThread, - controller_, texture_buffer, video_frame, timestamp)); -} - -VideoCaptureTextureWrapper::TextureWrapperDelegate::~TextureWrapperDelegate() { - // Might not be running on capture_task_runner_'s thread. Ensure owned objects - // are destroyed on the correct threads. - while (!gpu_memory_buffers_.empty()) { - capture_task_runner_->DeleteSoon(FROM_HERE, - gpu_memory_buffers_.front().release()); - gpu_memory_buffers_.pop(); - } - if (gl_helper_) - capture_task_runner_->DeleteSoon(FROM_HERE, gl_helper_.release()); - - if (capture_thread_context_) { - capture_task_runner_->PostTask( - FROM_HERE, - base::Bind(&ResetLostContextCallback, capture_thread_context_)); - capture_thread_context_->AddRef(); - ContextProviderCommandBuffer* raw_capture_thread_context = - capture_thread_context_.get(); - capture_thread_context_ = nullptr; - capture_task_runner_->ReleaseSoon(FROM_HERE, raw_capture_thread_context); - } -} - -void VideoCaptureTextureWrapper::TextureWrapperDelegate::Init( - const media::VideoCaptureFormat& capture_format) { - DCHECK(capture_task_runner_->BelongsToCurrentThread()); - - // BrowserGpuMemoryBufferManager::current() may not be accessed on IO Thread. - // TODO(mcasas): At this point |frame_format| represents the format we want to - // get from the VCDevice, but this might send another format. - for (size_t i = 0; i < kNumGpuMemoryBuffers; ++i) { - linked_ptr<gfx::GpuMemoryBuffer> gpu_memory_buffer( - BrowserGpuMemoryBufferManager::current()->AllocateGpuMemoryBuffer( - capture_format.frame_size, - gfx::GpuMemoryBuffer::BGRA_8888, - gfx::GpuMemoryBuffer::MAP).release()); - if (!gpu_memory_buffer.get()) { - OnError("Could not allocate GpuMemoryBuffer"); - while(!gpu_memory_buffers_.empty()) - gpu_memory_buffers_.pop(); - return; - } - gpu_memory_buffers_.push(gpu_memory_buffer); - } - - // In threaded compositing mode, we have to create our own context for Capture - // to avoid using the GPU command queue from multiple threads. Context - // creation must happen on UI thread; then the context needs to be bound to - // the appropriate thread, which is done in CreateGlHelper(). - BrowserThread::PostTask( - BrowserThread::UI, FROM_HERE, - base::Bind(&CreateContextOnUIThread, - media::BindToCurrentLoop( - base::Bind(&VideoCaptureTextureWrapper:: - TextureWrapperDelegate::CreateGlHelper, - this)))); -} - -void VideoCaptureTextureWrapper::TextureWrapperDelegate::CreateGlHelper( - scoped_refptr<ContextProviderCommandBuffer> capture_thread_context) { - DCHECK(capture_task_runner_->BelongsToCurrentThread()); - - if (!capture_thread_context.get()) { - DLOG(ERROR) << "No offscreen GL Context!"; - return; - } - // This may not happen in IO Thread. The destructor resets the context lost - // callback, so base::Unretained is safe; otherwise it'd be a circular ref - // counted dependency. - capture_thread_context->SetLostContextCallback(media::BindToCurrentLoop( - base::Bind( - &VideoCaptureTextureWrapper::TextureWrapperDelegate:: - LostContextCallback, - base::Unretained(this)))); - if (!capture_thread_context->BindToCurrentThread()) { - capture_thread_context = NULL; - DLOG(ERROR) << "Couldn't bind the Capture Context to the Capture Thread."; - return; - } - DCHECK(capture_thread_context); - capture_thread_context_ = capture_thread_context; - - // At this point, |capture_thread_context| is a cc::ContextProvider. Creation - // of our GLHelper should happen on Capture Thread. - gl_helper_.reset(new GLHelper(capture_thread_context->ContextGL(), - capture_thread_context->ContextSupport())); - DCHECK(gl_helper_); -} - -void VideoCaptureTextureWrapper::TextureWrapperDelegate::ReleaseCallback( - GLuint image_id, - GLuint texture_id, - linked_ptr<gfx::GpuMemoryBuffer> memory_buffer, - uint32 sync_point) { - DCHECK(capture_task_runner_->BelongsToCurrentThread()); - - // TODO(mcasas): Before recycling |memory_buffer| we have to make sure it has - // been consumed and fully used. - gpu_memory_buffers_.push(memory_buffer); - - if (gl_helper_) { - gl_helper_->DeleteTexture(texture_id); - capture_thread_context_->ContextGL()->DestroyImageCHROMIUM(image_id); - } -} - -void VideoCaptureTextureWrapper::TextureWrapperDelegate::LostContextCallback() { - DCHECK(capture_task_runner_->BelongsToCurrentThread()); - // Prevent incoming frames from being processed while OnError gets groked. - gl_helper_.reset(); - OnError("GLContext lost"); -} - -void VideoCaptureTextureWrapper::TextureWrapperDelegate::OnError( - const std::string& message) { - DCHECK(capture_task_runner_->BelongsToCurrentThread()); - DLOG(ERROR) << message; - BrowserThread::PostTask( - BrowserThread::IO, FROM_HERE, - base::Bind(&VideoCaptureController::DoErrorOnIOThread, controller_)); -} - -} // namespace content
diff --git a/content/browser/renderer_host/media/video_capture_texture_wrapper.h b/content/browser/renderer_host/media/video_capture_texture_wrapper.h deleted file mode 100644 index db6c1c5..0000000 --- a/content/browser/renderer_host/media/video_capture_texture_wrapper.h +++ /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. - -#ifndef CONTENT_BROWSER_RENDERER_HOST_MEDIA_VIDEO_CAPTURE_TEXTURE_WRAPPER_H_ -#define CONTENT_BROWSER_RENDERER_HOST_MEDIA_VIDEO_CAPTURE_TEXTURE_WRAPPER_H_ - -#include "base/memory/linked_ptr.h" -#include "base/memory/ref_counted.h" -#include "base/memory/scoped_ptr.h" -#include "base/single_thread_task_runner.h" -#include "content/browser/renderer_host/media/video_capture_device_client.h" -#include "content/common/content_export.h" -#include "media/video/capture/video_capture_device.h" - -namespace content { - -// VideoCaptureDeviceClient derived class for copying incoming memory-backed -// video data into a GpuMemoryBuffer (in turn into a VideoFrame), and sending it -// to |controller_|. Construction happens on IO Thread, whereas calls to -// OnIncomingCapturedData() come from wherever capture happens which might be, -// and often is, an OS thread different from Device Thread. -// -// It has an internal ref counted TextureWrapperDelegate class used to do all -// the work. This class creates and manages the necessary interactions with the -// GPU process for the texture uploading. This delegate is needed since the -// frame producer needs to own us, but we need to launch PostTask()s. -// -// TODO(mcasas): ctor |frame_format| is used for early GpuMemoryBuffer pool -// allocation, but VideoCaptureDevices might provide a different resolution when -// calling OnIncomingCapturedData(). What should we do then...? - -class CONTENT_EXPORT VideoCaptureTextureWrapper final - : public VideoCaptureDeviceClient { - public: - VideoCaptureTextureWrapper( - const base::WeakPtr<VideoCaptureController>& controller, - const scoped_refptr<VideoCaptureBufferPool>& buffer_pool, - const scoped_refptr<base::SingleThreadTaskRunner>& capture_task_runner, - const media::VideoCaptureFormat& capture_format); - ~VideoCaptureTextureWrapper() override; - - // Single media::VideoCaptureDevice::Client overriden method. - void OnIncomingCapturedData(const uint8* data, - int length, - const media::VideoCaptureFormat& frame_format, - int clockwise_rotation, - const base::TimeTicks& timestamp) override; - - private: - class TextureWrapperDelegate; - // Internal delegate doing most of the work. - scoped_refptr<TextureWrapperDelegate> wrapper_delegate_; - // Reference to Capture Thread task runner. - const scoped_refptr<base::SingleThreadTaskRunner> capture_task_runner_; - - DISALLOW_COPY_AND_ASSIGN(VideoCaptureTextureWrapper); -}; - -} // namespace content - -#endif // CONTENT_BROWSER_RENDERER_HOST_MEDIA_VIDEO_CAPTURE_TEXTURE_WRAPPER_H_
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc index 1e20d8e..467adf90 100644 --- a/content/browser/renderer_host/render_process_host_impl.cc +++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -35,6 +35,7 @@ #include "base/trace_event/trace_event.h" #include "base/tracked_objects.h" #include "cc/base/switches.h" +#include "components/scheduler/common/scheduler_switches.h" #include "content/browser/appcache/appcache_dispatcher_host.h" #include "content/browser/appcache/chrome_appcache_service.h" #include "content/browser/bad_message.h" @@ -1218,7 +1219,6 @@ switches::kDisable3DAPIs, switches::kDisableAcceleratedJpegDecoding, switches::kDisableAcceleratedVideoDecode, - switches::kDisableBlinkScheduler, switches::kDisableBlinkFeatures, switches::kDisableBreakpad, switches::kDisablePreferCompositingToLCDText, @@ -1356,6 +1356,9 @@ cc::switches::kStrictLayerPropertyChangeChecking, cc::switches::kTopControlsHideThreshold, cc::switches::kTopControlsShowThreshold, + + scheduler::switches::kDisableBlinkScheduler, + #if defined(ENABLE_PLUGINS) switches::kEnablePepperTesting, #endif
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 8599a31..021f4214 100644 --- a/content/browser/renderer_host/render_widget_host_view_mac.mm +++ b/content/browser/renderer_host/render_widget_host_view_mac.mm
@@ -3311,8 +3311,13 @@ // doesn't call any NSTextInput functions, such as setMarkedText or // insertText. So, we need to send an IPC message to a renderer so it can // delete the composition node. + // TODO(erikchen): NSInputManager is deprecated since OSX 10.6. Switch to + // NSTextInputContext. http://www.crbug.com/479010. +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" NSInputManager *currentInputManager = [NSInputManager currentInputManager]; [currentInputManager markedTextAbandoned:self]; +#pragma clang diagnostic pop hasMarkedText_ = NO; // Should not call [self unmarkText] here, because it'll send unnecessary
diff --git a/content/browser/security_exploit_browsertest.cc b/content/browser/security_exploit_browsertest.cc index 62d3b62c..0044aa4 100644 --- a/content/browser/security_exploit_browsertest.cc +++ b/content/browser/security_exploit_browsertest.cc
@@ -70,7 +70,7 @@ WebContentsImpl* wc = static_cast<WebContentsImpl*>(shell->web_contents()); wc->GetFrameTree()->root()->navigator()->RequestOpenURL( wc->GetFrameTree()->root()->current_frame_host(), extension_url, nullptr, - Referrer(), CURRENT_TAB, false, true); + Referrer(), ui::PAGE_TRANSITION_LINK, CURRENT_TAB, false, true); // Since the navigation above requires a cross-process swap, there will be a // pending RenderViewHost. Ensure it exists and is in a different process
diff --git a/content/browser/service_worker/service_worker_browsertest.cc b/content/browser/service_worker/service_worker_browsertest.cc index d2b0048..3c75a90 100644 --- a/content/browser/service_worker/service_worker_browsertest.cc +++ b/content/browser/service_worker/service_worker_browsertest.cc
@@ -160,8 +160,7 @@ void OnVersionStateChanged(int64 version_id, ServiceWorkerVersion::Status) override { DCHECK_CURRENTLY_ON(BrowserThread::IO); - const ServiceWorkerVersion* version = - context_->context()->GetLiveVersion(version_id); + const ServiceWorkerVersion* version = context_->GetLiveVersion(version_id); if (version->status() == ServiceWorkerVersion::ACTIVATED) { context_->RemoveObserver(this); BrowserThread::PostTask(BrowserThread::UI, @@ -249,7 +248,7 @@ *num_resources = -1; std::vector<ServiceWorkerRegistrationInfo> infos = - wrapper->context()->GetAllLiveRegistrationInfo(); + wrapper->GetAllLiveRegistrationInfo(); if (infos.empty()) return; @@ -267,8 +266,7 @@ else return; - ServiceWorkerVersion* version = - wrapper->context()->GetLiveVersion(version_id); + ServiceWorkerVersion* version = wrapper->GetLiveVersion(version_id); *num_resources = static_cast<int>(version->script_cache_map()->size()); } @@ -1097,12 +1095,10 @@ void FindRegistrationOnIO(const GURL& document_url, ServiceWorkerStatusCode* status, const base::Closure& continuation) { - wrapper()->context()->storage()->FindRegistrationForDocument( + wrapper()->FindRegistrationForDocument( document_url, base::Bind(&ServiceWorkerBlackBoxBrowserTest::FindRegistrationOnIO2, - this, - status, - continuation)); + this, status, continuation)); } void FindRegistrationOnIO2(
diff --git a/content/browser/service_worker/service_worker_context_watcher.cc b/content/browser/service_worker/service_worker_context_watcher.cc index 5d1a1a8..7c69ea22 100644 --- a/content/browser/service_worker/service_worker_context_watcher.cc +++ b/content/browser/service_worker/service_worker_context_watcher.cc
@@ -54,7 +54,7 @@ void ServiceWorkerContextWatcher::GetStoredRegistrationsOnIOThread() { DCHECK_CURRENTLY_ON(BrowserThread::IO); - context_->context()->storage()->GetAllRegistrations(base::Bind( + context_->GetAllRegistrations(base::Bind( &ServiceWorkerContextWatcher::OnStoredRegistrationsOnIOThread, this)); } @@ -67,11 +67,9 @@ registration_info_map; for (const auto& registration : stored_registrations) StoreRegistrationInfo(registration, ®istration_info_map); - for (const auto& registration : - context_->context()->GetAllLiveRegistrationInfo()) { + for (const auto& registration : context_->GetAllLiveRegistrationInfo()) StoreRegistrationInfo(registration, ®istration_info_map); - } - for (const auto& version : context_->context()->GetAllLiveVersionInfo()) + for (const auto& version : context_->GetAllLiveVersionInfo()) StoreVersionInfo(version); std::vector<ServiceWorkerRegistrationInfo> registrations;
diff --git a/content/browser/service_worker/service_worker_context_wrapper.cc b/content/browser/service_worker/service_worker_context_wrapper.cc index 0c09251..db72622 100644 --- a/content/browser/service_worker/service_worker_context_wrapper.cc +++ b/content/browser/service_worker/service_worker_context_wrapper.cc
@@ -147,11 +147,6 @@ base::Bind(&ServiceWorkerContextWrapper::DidDeleteAndStartOver, this)); } -ServiceWorkerContextCore* ServiceWorkerContextWrapper::context() { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - return context_core_.get(); -} - StoragePartitionImpl* ServiceWorkerContextWrapper::storage_partition() const { DCHECK_CURRENTLY_ON(BrowserThread::UI); return storage_partition_; @@ -432,6 +427,125 @@ this, other_url, callback)); } +ServiceWorkerRegistration* ServiceWorkerContextWrapper::GetLiveRegistration( + int64_t registration_id) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + if (!context_core_) + return nullptr; + return context_core_->GetLiveRegistration(registration_id); +} + +ServiceWorkerVersion* ServiceWorkerContextWrapper::GetLiveVersion( + int64_t version_id) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + if (!context_core_) + return nullptr; + return context_core_->GetLiveVersion(version_id); +} + +std::vector<ServiceWorkerRegistrationInfo> +ServiceWorkerContextWrapper::GetAllLiveRegistrationInfo() { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + if (!context_core_) + return std::vector<ServiceWorkerRegistrationInfo>(); + return context_core_->GetAllLiveRegistrationInfo(); +} + +std::vector<ServiceWorkerVersionInfo> +ServiceWorkerContextWrapper::GetAllLiveVersionInfo() { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + if (!context_core_) + return std::vector<ServiceWorkerVersionInfo>(); + return context_core_->GetAllLiveVersionInfo(); +} + +void ServiceWorkerContextWrapper::FindRegistrationForDocument( + const GURL& document_url, + const FindRegistrationCallback& callback) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + if (!context_core_) { + // FindRegistrationForDocument() can run the callback synchronously. + callback.Run(SERVICE_WORKER_ERROR_ABORT, nullptr); + return; + } + context_core_->storage()->FindRegistrationForDocument(document_url, callback); +} + +void ServiceWorkerContextWrapper::FindRegistrationForId( + int64_t registration_id, + const GURL& origin, + const FindRegistrationCallback& callback) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + if (!context_core_) { + // FindRegistrationForId() can run the callback synchronously. + callback.Run(SERVICE_WORKER_ERROR_ABORT, nullptr); + return; + } + context_core_->storage()->FindRegistrationForId(registration_id, origin, + callback); +} + +void ServiceWorkerContextWrapper::GetAllRegistrations( + const GetRegistrationsInfosCallback& callback) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + if (!context_core_) { + RunSoon(base::Bind(callback, std::vector<ServiceWorkerRegistrationInfo>())); + return; + } + context_core_->storage()->GetAllRegistrations(callback); +} + +void ServiceWorkerContextWrapper::GetRegistrationUserData( + int64_t registration_id, + const std::string& key, + const GetUserDataCallback& callback) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + if (!context_core_) { + RunSoon(base::Bind(callback, std::string(), SERVICE_WORKER_ERROR_ABORT)); + return; + } + context_core_->storage()->GetUserData(registration_id, key, callback); +} + +void ServiceWorkerContextWrapper::StoreRegistrationUserData( + int64_t registration_id, + const GURL& origin, + const std::string& key, + const std::string& data, + const StatusCallback& callback) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + if (!context_core_) { + RunSoon(base::Bind(callback, SERVICE_WORKER_ERROR_ABORT)); + return; + } + context_core_->storage()->StoreUserData(registration_id, origin, key, data, + callback); +} + +void ServiceWorkerContextWrapper::ClearRegistrationUserData( + int64_t registration_id, + const std::string& key, + const StatusCallback& callback) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + if (!context_core_) { + RunSoon(base::Bind(callback, SERVICE_WORKER_ERROR_ABORT)); + return; + } + context_core_->storage()->ClearUserData(registration_id, key, callback); +} + +void ServiceWorkerContextWrapper::GetUserDataForAllRegistrations( + const std::string& key, + const GetUserDataForAllRegistrationsCallback& callback) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + if (!context_core_) { + RunSoon(base::Bind(callback, std::vector<std::pair<int64_t, std::string>>(), + SERVICE_WORKER_ERROR_ABORT)); + return; + } + context_core_->storage()->GetUserDataForAllRegistrations(key, callback); +} + void ServiceWorkerContextWrapper::AddObserver( ServiceWorkerContextObserver* observer) { observer_list_->AddObserver(observer); @@ -497,4 +611,9 @@ &ServiceWorkerContextObserver::OnStorageWiped); } +ServiceWorkerContextCore* ServiceWorkerContextWrapper::context() { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + return context_core_.get(); +} + } // namespace content
diff --git a/content/browser/service_worker/service_worker_context_wrapper.h b/content/browser/service_worker/service_worker_context_wrapper.h index f77b07c..eca4a36 100644 --- a/content/browser/service_worker/service_worker_context_wrapper.h +++ b/content/browser/service_worker/service_worker_context_wrapper.h
@@ -40,7 +40,15 @@ : NON_EXPORTED_BASE(public ServiceWorkerContext), public base::RefCountedThreadSafe<ServiceWorkerContextWrapper> { public: - typedef base::Callback<void(ServiceWorkerStatusCode)> StatusCallback; + using StatusCallback = base::Callback<void(ServiceWorkerStatusCode)>; + using FindRegistrationCallback = + ServiceWorkerStorage::FindRegistrationCallback; + using GetRegistrationsInfosCallback = + ServiceWorkerStorage::GetRegistrationsInfosCallback; + using GetUserDataCallback = ServiceWorkerStorage::GetUserDataCallback; + using GetUserDataForAllRegistrationsCallback = + ServiceWorkerStorage::GetUserDataForAllRegistrationsCallback; + ServiceWorkerContextWrapper(BrowserContext* browser_context); // Init and Shutdown are for use on the UI thread when the profile, @@ -55,11 +63,6 @@ // called on the IO thread. void DeleteAndStartOver(); - // The core context is only for use on the IO thread. - // Can be null before/during init, during/after shutdown, and after - // DeleteAndStartOver fails. - ServiceWorkerContextCore* context(); - // The StoragePartition should only be used on the UI thread. // Can be null before/during init and during/after shutdown. StoragePartitionImpl* storage_partition() const; @@ -88,6 +91,32 @@ const GURL& other_url, const CheckHasServiceWorkerCallback& callback) override; + ServiceWorkerRegistration* GetLiveRegistration(int64_t registration_id); + ServiceWorkerVersion* GetLiveVersion(int64_t version_id); + std::vector<ServiceWorkerRegistrationInfo> GetAllLiveRegistrationInfo(); + std::vector<ServiceWorkerVersionInfo> GetAllLiveVersionInfo(); + + void FindRegistrationForDocument(const GURL& document_url, + const FindRegistrationCallback& callback); + void FindRegistrationForId(int64_t registration_id, + const GURL& origin, + const FindRegistrationCallback& callback); + void GetAllRegistrations(const GetRegistrationsInfosCallback& callback); + void GetRegistrationUserData(int64_t registration_id, + const std::string& key, + const GetUserDataCallback& callback); + void StoreRegistrationUserData(int64_t registration_id, + const GURL& origin, + const std::string& key, + const std::string& data, + const StatusCallback& callback); + void ClearRegistrationUserData(int64_t registration_id, + const std::string& key, + const StatusCallback& callback); + void GetUserDataForAllRegistrations( + const std::string& key, + const GetUserDataForAllRegistrationsCallback& callback); + // DeleteForOrigin with completion callback. Does not exit early, and returns // false if one or more of the deletions fail. virtual void DeleteForOrigin(const GURL& origin_url, @@ -104,7 +133,12 @@ friend class BackgroundSyncManagerTest; friend class base::RefCountedThreadSafe<ServiceWorkerContextWrapper>; friend class EmbeddedWorkerTestHelper; + friend class EmbeddedWorkerBrowserTest; + friend class ServiceWorkerDispatcherHost; + friend class ServiceWorkerInternalsUI; friend class ServiceWorkerProcessManager; + friend class ServiceWorkerRequestHandler; + friend class ServiceWorkerVersionBrowserTest; friend class MockServiceWorkerContextWrapper; ~ServiceWorkerContextWrapper() override; @@ -133,6 +167,11 @@ ServiceWorkerStatusCode status, const scoped_refptr<content::ServiceWorkerRegistration>& registration); + // The core context is only for use on the IO thread. + // Can be null before/during init, during/after shutdown, and after + // DeleteAndStartOver fails. + ServiceWorkerContextCore* context(); + const scoped_refptr<ObserverListThreadSafe<ServiceWorkerContextObserver> > observer_list_; const scoped_ptr<ServiceWorkerProcessManager> process_manager_;
diff --git a/content/browser/service_worker/service_worker_fetch_dispatcher.cc b/content/browser/service_worker/service_worker_fetch_dispatcher.cc index 14e0ceec..771cbb1 100644 --- a/content/browser/service_worker/service_worker_fetch_dispatcher.cc +++ b/content/browser/service_worker/service_worker_fetch_dispatcher.cc
@@ -86,7 +86,7 @@ request_.get()); DCHECK(!fetch_callback_.is_null()); FetchCallback fetch_callback = fetch_callback_; - fetch_callback.Run(status, fetch_result, response); + fetch_callback.Run(status, fetch_result, response, version_); } } // namespace content
diff --git a/content/browser/service_worker/service_worker_fetch_dispatcher.h b/content/browser/service_worker/service_worker_fetch_dispatcher.h index f20fb94..f0165b6 100644 --- a/content/browser/service_worker/service_worker_fetch_dispatcher.h +++ b/content/browser/service_worker/service_worker_fetch_dispatcher.h
@@ -20,7 +20,9 @@ public: typedef base::Callback<void(ServiceWorkerStatusCode, ServiceWorkerFetchEventResult, - const ServiceWorkerResponse&)> FetchCallback; + const ServiceWorkerResponse&, + scoped_refptr<ServiceWorkerVersion>)> + FetchCallback; ServiceWorkerFetchDispatcher(scoped_ptr<ServiceWorkerFetchRequest> request, ServiceWorkerVersion* version,
diff --git a/content/browser/service_worker/service_worker_internals_ui.cc b/content/browser/service_worker/service_worker_internals_ui.cc index 0a0df729..2176eb75 100644 --- a/content/browser/service_worker/service_worker_internals_ui.cc +++ b/content/browser/service_worker/service_worker_internals_ui.cc
@@ -78,13 +78,8 @@ return; } - if (!context->context()) { - callback.Run(SERVICE_WORKER_ERROR_ABORT); - return; - } - scoped_refptr<ServiceWorkerVersion> version = - context->context()->GetLiveVersion(version_id); + context->GetLiveVersion(version_id); if (!version.get()) { callback.Run(SERVICE_WORKER_ERROR_NOT_FOUND); return; @@ -107,13 +102,8 @@ return; } - if (!context->context()) { - callback.Run(SERVICE_WORKER_ERROR_ABORT); - return; - } - scoped_refptr<ServiceWorkerVersion> version = - context->context()->GetLiveVersion(version_id); + context->GetLiveVersion(version_id); if (!version.get()) { callback.Run(SERVICE_WORKER_ERROR_NOT_FOUND); return; @@ -122,45 +112,6 @@ version->DispatchPushEvent(callback, data); } -void UnregisterWithScope( - scoped_refptr<ServiceWorkerContextWrapper> context, - const GURL& scope, - const ServiceWorkerInternalsUI::StatusCallback& callback) { - if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) { - BrowserThread::PostTask( - BrowserThread::IO, - FROM_HERE, - base::Bind(UnregisterWithScope, context, scope, callback)); - return; - } - - if (!context->context()) { - callback.Run(SERVICE_WORKER_ERROR_ABORT); - return; - } - context->context()->UnregisterServiceWorker(scope, callback); -} - -void FindRegistrationForPattern( - scoped_refptr<ServiceWorkerContextWrapper> context, - const GURL& scope, - const ServiceWorkerStorage::FindRegistrationCallback callback) { - if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) { - BrowserThread::PostTask( - BrowserThread::IO, - FROM_HERE, - base::Bind(FindRegistrationForPattern, context, scope, callback)); - return; - } - - if (!context->context()) { - callback.Run(SERVICE_WORKER_ERROR_ABORT, - scoped_refptr<ServiceWorkerRegistration>()); - return; - } - context->context()->storage()->FindRegistrationForPattern(scope, callback); -} - void UpdateVersionInfo(const ServiceWorkerVersionInfo& version, DictionaryValue* info) { switch (version.running_status) { @@ -256,37 +207,17 @@ const GetRegistrationsCallback& callback, const std::vector<ServiceWorkerRegistrationInfo>& stored_registrations) { DCHECK_CURRENTLY_ON(BrowserThread::IO); - if (!context->context()) { - BrowserThread::PostTask( - BrowserThread::UI, FROM_HERE, - base::Bind(callback, std::vector<ServiceWorkerRegistrationInfo>(), - std::vector<ServiceWorkerVersionInfo>(), - std::vector<ServiceWorkerRegistrationInfo>())); - return; - } - BrowserThread::PostTask( - BrowserThread::UI, - FROM_HERE, - base::Bind(callback, - context->context()->GetAllLiveRegistrationInfo(), - context->context()->GetAllLiveVersionInfo(), - stored_registrations)); + BrowserThread::UI, FROM_HERE, + base::Bind(callback, context->GetAllLiveRegistrationInfo(), + context->GetAllLiveVersionInfo(), stored_registrations)); } void GetRegistrationsOnIOThread( scoped_refptr<ServiceWorkerContextWrapper> context, const GetRegistrationsCallback& callback) { DCHECK_CURRENTLY_ON(BrowserThread::IO); - if (!context->context()) { - BrowserThread::PostTask( - BrowserThread::UI, FROM_HERE, - base::Bind(callback, std::vector<ServiceWorkerRegistrationInfo>(), - std::vector<ServiceWorkerVersionInfo>(), - std::vector<ServiceWorkerRegistrationInfo>())); - return; - } - context->context()->storage()->GetAllRegistrations( + context->GetAllRegistrations( base::Bind(DidGetStoredRegistrationsOnIOThread, context, callback)); } @@ -669,4 +600,26 @@ context->StartServiceWorker(GURL(scope_string), callback); } +void ServiceWorkerInternalsUI::UnregisterWithScope( + scoped_refptr<ServiceWorkerContextWrapper> context, + const GURL& scope, + const ServiceWorkerInternalsUI::StatusCallback& callback) const { + if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) { + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + base::Bind(&ServiceWorkerInternalsUI::UnregisterWithScope, + base::Unretained(this), context, scope, callback)); + return; + } + + if (!context->context()) { + callback.Run(SERVICE_WORKER_ERROR_ABORT); + return; + } + + // ServiceWorkerContextWrapper::UnregisterServiceWorker doesn't work here + // because that reduces a status code to boolean. + context->context()->UnregisterServiceWorker(scope, callback); +} + } // namespace content
diff --git a/content/browser/service_worker/service_worker_internals_ui.h b/content/browser/service_worker/service_worker_internals_ui.h index 40ceb38..82040f0 100644 --- a/content/browser/service_worker/service_worker_internals_ui.h +++ b/content/browser/service_worker/service_worker_internals_ui.h
@@ -64,6 +64,10 @@ StoragePartition** result_partition, StoragePartition* storage_partition) const; + void UnregisterWithScope(scoped_refptr<ServiceWorkerContextWrapper> context, + const GURL& scope, + const StatusCallback& callback) const; + base::ScopedPtrHashMap<uintptr_t, PartitionObserver> observers_; int next_partition_id_; };
diff --git a/content/browser/service_worker/service_worker_registration.cc b/content/browser/service_worker/service_worker_registration.cc index 26d4528..d2b5833 100644 --- a/content/browser/service_worker/service_worker_registration.cc +++ b/content/browser/service_worker/service_worker_registration.cc
@@ -259,10 +259,8 @@ scoped_refptr<ServiceWorkerVersion> activating_version = waiting_version(); scoped_refptr<ServiceWorkerVersion> exiting_version = active_version(); - if (activating_version->is_doomed() || - activating_version->status() == ServiceWorkerVersion::REDUNDANT) { + if (activating_version->is_redundant()) return; // Activation is no longer relevant. - } // "5. If exitingWorker is not null, if (exiting_version.get()) {
diff --git a/content/browser/service_worker/service_worker_storage_unittest.cc b/content/browser/service_worker/service_worker_storage_unittest.cc index 7d53c2d..aa07b5b3 100644 --- a/content/browser/service_worker/service_worker_storage_unittest.cc +++ b/content/browser/service_worker/service_worker_storage_unittest.cc
@@ -1116,7 +1116,6 @@ &verify_ids, &was_called, &result)); - registration_->active_version()->Doom(); base::RunLoop().RunUntilIdle(); ASSERT_TRUE(was_called); EXPECT_EQ(SERVICE_WORKER_OK, result); @@ -1131,6 +1130,7 @@ // Removing the controllee should cause the resources to be deleted. registration_->active_version()->RemoveControllee(host.get()); + registration_->active_version()->Doom(); base::RunLoop().RunUntilIdle(); verify_ids.clear(); EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, @@ -1268,7 +1268,6 @@ &verify_ids, &was_called, &result)); - registration_->active_version()->Doom(); base::RunLoop().RunUntilIdle(); ASSERT_TRUE(was_called); EXPECT_EQ(SERVICE_WORKER_OK, result); @@ -1284,6 +1283,7 @@ // Removing the controllee should cause the old version's resources to be // deleted. registration_->active_version()->RemoveControllee(host.get()); + registration_->active_version()->Doom(); base::RunLoop().RunUntilIdle(); verify_ids.clear(); EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK,
diff --git a/content/browser/service_worker/service_worker_url_request_job.cc b/content/browser/service_worker/service_worker_url_request_job.cc index 8d848f4..c6bdfd7 100644 --- a/content/browser/service_worker/service_worker_url_request_job.cc +++ b/content/browser/service_worker/service_worker_url_request_job.cc
@@ -477,7 +477,8 @@ void ServiceWorkerURLRequestJob::DidDispatchFetchEvent( ServiceWorkerStatusCode status, ServiceWorkerFetchEventResult fetch_result, - const ServiceWorkerResponse& response) { + const ServiceWorkerResponse& response, + scoped_refptr<ServiceWorkerVersion> version) { fetch_dispatcher_.reset(); // Check if we're not orphaned. @@ -529,16 +530,16 @@ // TODO(horo): When we support mixed-content (HTTP) no-cors requests from a // ServiceWorker, we have to check the security level of the responses. DCHECK(!http_response_info_); + DCHECK(version); const net::HttpResponseInfo* main_script_http_info = - provider_host_->active_version()->GetMainScriptHttpResponseInfo(); + version->GetMainScriptHttpResponseInfo(); DCHECK(main_script_http_info); http_response_info_.reset(new net::HttpResponseInfo(*main_script_http_info)); // Set up a request for reading the stream. if (response.stream_url.is_valid()) { DCHECK(response.blob_uuid.empty()); - DCHECK(provider_host_->active_version()); - streaming_version_ = provider_host_->active_version(); + streaming_version_ = version; streaming_version_->AddStreamingURLRequestJob(this); response_url_ = response.url; service_worker_response_type_ = response.response_type;
diff --git a/content/browser/service_worker/service_worker_url_request_job.h b/content/browser/service_worker/service_worker_url_request_job.h index 0081740..b9faa7a0 100644 --- a/content/browser/service_worker/service_worker_url_request_job.h +++ b/content/browser/service_worker/service_worker_url_request_job.h
@@ -144,7 +144,8 @@ void DidPrepareFetchEvent(); void DidDispatchFetchEvent(ServiceWorkerStatusCode status, ServiceWorkerFetchEventResult fetch_result, - const ServiceWorkerResponse& response); + const ServiceWorkerResponse& response, + scoped_refptr<ServiceWorkerVersion> version); // Populates |http_response_headers_|. void CreateResponseHeader(int status_code,
diff --git a/content/browser/service_worker/service_worker_url_request_job_unittest.cc b/content/browser/service_worker/service_worker_url_request_job_unittest.cc index 1f3dd48..c004bf0 100644 --- a/content/browser/service_worker/service_worker_url_request_job_unittest.cc +++ b/content/browser/service_worker/service_worker_url_request_job_unittest.cc
@@ -235,6 +235,39 @@ TestRequest(200, "OK", std::string()); } +class ProviderDeleteHelper : public EmbeddedWorkerTestHelper { + public: + ProviderDeleteHelper(int mock_render_process_id) + : EmbeddedWorkerTestHelper(base::FilePath(), mock_render_process_id) {} + ~ProviderDeleteHelper() override {} + + protected: + void OnFetchEvent(int embedded_worker_id, + int request_id, + const ServiceWorkerFetchRequest& request) override { + context()->RemoveProviderHost(kProcessID, kProviderID); + SimulateSend(new ServiceWorkerHostMsg_FetchEventFinished( + embedded_worker_id, request_id, + SERVICE_WORKER_FETCH_EVENT_RESULT_RESPONSE, + ServiceWorkerResponse( + GURL(), 200, "OK", blink::WebServiceWorkerResponseTypeDefault, + ServiceWorkerHeaderMap(), std::string(), 0, GURL()))); + } + + private: + DISALLOW_COPY_AND_ASSIGN(ProviderDeleteHelper); +}; + +TEST_F(ServiceWorkerURLRequestJobTest, DeletedProviderHost) { + version_->SetStatus(ServiceWorkerVersion::ACTIVATED); + // Shouldn't crash if the ProviderHost is deleted prior to completion of + // the fetch event. + SetUpWithHelper(new ProviderDeleteHelper(kProcessID)); + + version_->SetStatus(ServiceWorkerVersion::ACTIVATED); + TestRequest(200, "OK", std::string()); +} + // Responds to fetch events with a blob. class BlobResponder : public EmbeddedWorkerTestHelper { public:
diff --git a/content/browser/service_worker/service_worker_version.cc b/content/browser/service_worker/service_worker_version.cc index f0a9c17..de905b67 100644 --- a/content/browser/service_worker/service_worker_version.cc +++ b/content/browser/service_worker/service_worker_version.cc
@@ -806,10 +806,6 @@ if (HasControllee()) return; FOR_EACH_OBSERVER(Listener, listeners_, OnNoControllees(this)); - if (is_doomed_) { - DoomInternal(); - return; - } } void ServiceWorkerVersion::AddStreamingURLRequestJob( @@ -822,7 +818,7 @@ void ServiceWorkerVersion::RemoveStreamingURLRequestJob( const ServiceWorkerURLRequestJob* request_job) { streaming_url_request_jobs_.erase(request_job); - if (is_doomed_) + if (is_redundant()) StopWorkerIfIdle(); } @@ -845,11 +841,14 @@ } void ServiceWorkerVersion::Doom() { - if (is_doomed_) + DCHECK(!HasControllee()); + SetStatus(REDUNDANT); + StopWorkerIfIdle(); + if (!context_) return; - is_doomed_ = true; - if (!HasControllee()) - DoomInternal(); + std::vector<ServiceWorkerDatabase::ResourceRecord> resources; + script_cache_map_.GetResources(&resources); + context_->storage()->PurgeResources(resources); } void ServiceWorkerVersion::SetDevToolsAttached(bool attached) { @@ -927,7 +926,7 @@ DCHECK_EQ(STOPPED, running_status()); scoped_refptr<ServiceWorkerVersion> protect(this); - bool should_restart = !is_doomed() && !start_callbacks_.empty() && + bool should_restart = !is_redundant() && !start_callbacks_.empty() && (old_status != EmbeddedWorkerInstance::STARTING); StopTimeoutTimer(); @@ -971,7 +970,6 @@ FOR_EACH_OBSERVER(Listener, listeners_, OnRunningStateChanged(this)); - // Restart worker if we have any start callbacks and the worker isn't doomed. if (should_restart) StartWorkerInternal(false /* pause_after_download */); } @@ -1139,7 +1137,7 @@ scoped_refptr<ServiceWorkerVersion> protect(this); callback->Run(rv); - RemoveCallbackAndStopIfDoomed(&activate_callbacks_, request_id); + RemoveCallbackAndStopIfRedundant(&activate_callbacks_, request_id); } void ServiceWorkerVersion::OnInstallEventFinished( @@ -1162,7 +1160,7 @@ scoped_refptr<ServiceWorkerVersion> protect(this); callback->Run(status); - RemoveCallbackAndStopIfDoomed(&install_callbacks_, request_id); + RemoveCallbackAndStopIfRedundant(&install_callbacks_, request_id); } void ServiceWorkerVersion::OnFetchEventFinished( @@ -1180,7 +1178,7 @@ scoped_refptr<ServiceWorkerVersion> protect(this); callback->Run(SERVICE_WORKER_OK, result, response); - RemoveCallbackAndStopIfDoomed(&fetch_callbacks_, request_id); + RemoveCallbackAndStopIfRedundant(&fetch_callbacks_, request_id); } void ServiceWorkerVersion::OnSyncEventFinished( @@ -1196,7 +1194,7 @@ scoped_refptr<ServiceWorkerVersion> protect(this); callback->Run(SERVICE_WORKER_OK); - RemoveCallbackAndStopIfDoomed(&sync_callbacks_, request_id); + RemoveCallbackAndStopIfRedundant(&sync_callbacks_, request_id); } void ServiceWorkerVersion::OnNotificationClickEventFinished( @@ -1212,7 +1210,7 @@ scoped_refptr<ServiceWorkerVersion> protect(this); callback->Run(SERVICE_WORKER_OK); - RemoveCallbackAndStopIfDoomed(¬ification_click_callbacks_, request_id); + RemoveCallbackAndStopIfRedundant(¬ification_click_callbacks_, request_id); } void ServiceWorkerVersion::OnPushEventFinished( @@ -1232,7 +1230,7 @@ scoped_refptr<ServiceWorkerVersion> protect(this); callback->Run(status); - RemoveCallbackAndStopIfDoomed(&push_callbacks_, request_id); + RemoveCallbackAndStopIfRedundant(&push_callbacks_, request_id); } void ServiceWorkerVersion::OnGeofencingEventFinished(int request_id) { @@ -1248,7 +1246,7 @@ scoped_refptr<ServiceWorkerVersion> protect(this); callback->Run(SERVICE_WORKER_OK); - RemoveCallbackAndStopIfDoomed(&geofencing_callbacks_, request_id); + RemoveCallbackAndStopIfRedundant(&geofencing_callbacks_, request_id); } void ServiceWorkerVersion::OnCrossOriginConnectEventFinished( @@ -1266,7 +1264,8 @@ scoped_refptr<ServiceWorkerVersion> protect(this); callback->Run(SERVICE_WORKER_OK, accept_connection); - RemoveCallbackAndStopIfDoomed(&cross_origin_connect_callbacks_, request_id); + RemoveCallbackAndStopIfRedundant(&cross_origin_connect_callbacks_, + request_id); } void ServiceWorkerVersion::OnOpenWindow(int request_id, GURL url) { @@ -1518,7 +1517,7 @@ const StatusCallback& callback, ServiceWorkerStatusCode status, const scoped_refptr<ServiceWorkerRegistration>& protect) { - if (status != SERVICE_WORKER_OK || is_doomed()) { + if (status != SERVICE_WORKER_OK || is_redundant()) { RecordStartWorkerResult(status); RunSoon(base::Bind(callback, SERVICE_WORKER_ERROR_START_WORKER_FAILED)); return; @@ -1746,9 +1745,9 @@ base::TimeTicks start_time = start_time_; ClearTick(&start_time_); - // Failing to start a doomed worker isn't interesting and very common when + // Failing to start a redundant worker isn't interesting and very common when // update dooms because the script is byte-to-byte identical. - if (is_doomed_ || status_ == REDUNDANT) + if (is_redundant()) return; ServiceWorkerMetrics::RecordStartWorkerStatus(status, IsInstalled(status_)); @@ -1783,25 +1782,12 @@ EmbeddedWorkerInstance::STARTING_PHASE_MAX_VALUE); } -void ServiceWorkerVersion::DoomInternal() { - DCHECK(is_doomed_); - DCHECK(!HasControllee()); - SetStatus(REDUNDANT); - StopWorkerIfIdle(); - if (!context_) - return; - std::vector<ServiceWorkerDatabase::ResourceRecord> resources; - script_cache_map_.GetResources(&resources); - context_->storage()->PurgeResources(resources); -} - template <typename IDMAP> -void ServiceWorkerVersion::RemoveCallbackAndStopIfDoomed( - IDMAP* callbacks, - int request_id) { +void ServiceWorkerVersion::RemoveCallbackAndStopIfRedundant(IDMAP* callbacks, + int request_id) { RestartTick(&idle_time_); callbacks->Remove(request_id); - if (is_doomed_) { + if (is_redundant()) { // The stop should be already scheduled, but try to stop immediately, in // order to release worker resources soon. StopWorkerIfIdle();
diff --git a/content/browser/service_worker/service_worker_version.h b/content/browser/service_worker/service_worker_version.h index 11810a8..ba13327 100644 --- a/content/browser/service_worker/service_worker_version.h +++ b/content/browser/service_worker/service_worker_version.h
@@ -286,11 +286,10 @@ void ReportError(ServiceWorkerStatusCode status, const std::string& status_message); - // Dooms this version to have REDUNDANT status and its resources deleted. If - // the version is controlling a page, these changes will happen when the - // version no longer controls any pages. + // Sets this version's status to REDUNDANT and deletes its resources. + // The version must not have controllees. void Doom(); - bool is_doomed() const { return is_doomed_; } + bool is_redundant() const { return status_ == REDUNDANT; } bool skip_waiting() const { return skip_waiting_; } @@ -472,10 +471,8 @@ // and records metrics about startup. void RecordStartWorkerResult(ServiceWorkerStatusCode status); - void DoomInternal(); - template <typename IDMAP> - void RemoveCallbackAndStopIfDoomed(IDMAP* callbacks, int request_id); + void RemoveCallbackAndStopIfRedundant(IDMAP* callbacks, int request_id); template <typename CallbackType> int AddRequest(const CallbackType& callback, @@ -539,7 +536,6 @@ // callback map). std::queue<RequestInfo> requests_; - bool is_doomed_ = false; bool skip_waiting_ = false; bool skip_recording_startup_time_ = false; bool force_bypass_cache_for_scripts_ = false;
diff --git a/content/browser/site_per_process_browsertest.cc b/content/browser/site_per_process_browsertest.cc index 3e76965..29ee0e1d 100644 --- a/content/browser/site_per_process_browsertest.cc +++ b/content/browser/site_per_process_browsertest.cc
@@ -18,6 +18,7 @@ #include "content/browser/renderer_host/render_view_host_impl.h" #include "content/browser/web_contents/web_contents_impl.h" #include "content/common/frame_messages.h" +#include "content/public/browser/navigation_details.h" #include "content/public/browser/notification_observer.h" #include "content/public/browser/notification_service.h" #include "content/public/browser/notification_types.h" @@ -2044,4 +2045,33 @@ DepictFrameTree(root)); } +IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, + PageTransitionForSecondaryIframeNavigation) { + GURL main_url(embedded_test_server()->GetURL("/site_per_process_main.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(); + + TestNavigationObserver observer(shell()->web_contents()); + + // Load same-site page into iframe. + FrameTreeNode* child = root->child_at(0); + GURL http_url(embedded_test_server()->GetURL("/title1.html")); + NavigateFrameToURL(child, http_url); + EXPECT_EQ(http_url, observer.last_navigation_url()); + EXPECT_TRUE(observer.last_navigation_succeeded()); + + // Load cross-site page into iframe. + TestFrameNavigationObserver frame_observer(child, 1); + GURL url = embedded_test_server()->GetURL("foo.com", "/title2.html"); + NavigateIframeToURL(shell()->web_contents(), "test", url); + frame_observer.Wait(); + + EXPECT_EQ(NAVIGATION_TYPE_NEW_SUBFRAME, + frame_observer.load_committed_details().type); +} + } // namespace content
diff --git a/content/browser/speech/google_streaming_remote_engine.cc b/content/browser/speech/google_streaming_remote_engine.cc index ca8e906e..11c7ac7 100644 --- a/content/browser/speech/google_streaming_remote_engine.cc +++ b/content/browser/speech/google_streaming_remote_engine.cc
@@ -445,20 +445,17 @@ case proto::SpeechRecognitionEvent::STATUS_ABORTED: return Abort(SPEECH_RECOGNITION_ERROR_ABORTED); case proto::SpeechRecognitionEvent::STATUS_AUDIO_CAPTURE: - return Abort(SPEECH_RECOGNITION_ERROR_AUDIO); + return Abort(SPEECH_RECOGNITION_ERROR_AUDIO_CAPTURE); case proto::SpeechRecognitionEvent::STATUS_NETWORK: return Abort(SPEECH_RECOGNITION_ERROR_NETWORK); case proto::SpeechRecognitionEvent::STATUS_NOT_ALLOWED: - // TODO(hans): We need a better error code for this. - return Abort(SPEECH_RECOGNITION_ERROR_ABORTED); + return Abort(SPEECH_RECOGNITION_ERROR_NOT_ALLOWED); case proto::SpeechRecognitionEvent::STATUS_SERVICE_NOT_ALLOWED: - // TODO(hans): We need a better error code for this. - return Abort(SPEECH_RECOGNITION_ERROR_ABORTED); + return Abort(SPEECH_RECOGNITION_ERROR_SERVICE_NOT_ALLOWED); case proto::SpeechRecognitionEvent::STATUS_BAD_GRAMMAR: return Abort(SPEECH_RECOGNITION_ERROR_BAD_GRAMMAR); case proto::SpeechRecognitionEvent::STATUS_LANGUAGE_NOT_SUPPORTED: - // TODO(hans): We need a better error code for this. - return Abort(SPEECH_RECOGNITION_ERROR_ABORTED); + return Abort(SPEECH_RECOGNITION_ERROR_LANGUAGE_NOT_SUPPORTED); } }
diff --git a/content/browser/speech/speech_recognizer_impl.cc b/content/browser/speech/speech_recognizer_impl.cc index 7b1d53f..a08ffe17 100644 --- a/content/browser/speech/speech_recognizer_impl.cc +++ b/content/browser/speech/speech_recognizer_impl.cc
@@ -503,7 +503,7 @@ // TODO(xians): Check if the OS has the device with |device_id_|, return // |SPEECH_AUDIO_ERROR_DETAILS_NO_MIC| if the target device does not exist. if (!audio_manager->HasAudioInputDevices()) { - return Abort(SpeechRecognitionError(SPEECH_RECOGNITION_ERROR_AUDIO, + return Abort(SpeechRecognitionError(SPEECH_RECOGNITION_ERROR_AUDIO_CAPTURE, SPEECH_AUDIO_ERROR_DETAILS_NO_MIC)); } @@ -513,7 +513,8 @@ device_id_); if (!in_params.IsValid() && !unit_test_is_active) { DLOG(ERROR) << "Invalid native audio input parameters"; - return Abort(SpeechRecognitionError(SPEECH_RECOGNITION_ERROR_AUDIO)); + return Abort( + SpeechRecognitionError(SPEECH_RECOGNITION_ERROR_AUDIO_CAPTURE)); } // Audio converter shall provide audio based on these parameters as output. @@ -564,7 +565,8 @@ audio_manager, this, input_parameters, device_id_, NULL); if (!audio_controller_.get()) { - return Abort(SpeechRecognitionError(SPEECH_RECOGNITION_ERROR_AUDIO)); + return Abort( + SpeechRecognitionError(SPEECH_RECOGNITION_ERROR_AUDIO_CAPTURE)); } audio_log_->OnCreated(0, input_parameters, device_id_); @@ -649,7 +651,8 @@ SpeechRecognizerImpl::FSMState SpeechRecognizerImpl::AbortWithError(const FSMEventArgs& event_args) { if (event_args.event == EVENT_AUDIO_ERROR) { - return Abort(SpeechRecognitionError(SPEECH_RECOGNITION_ERROR_AUDIO)); + return Abort( + SpeechRecognitionError(SPEECH_RECOGNITION_ERROR_AUDIO_CAPTURE)); } else if (event_args.event == EVENT_ENGINE_ERROR) { return Abort(event_args.engine_error); }
diff --git a/content/browser/speech/speech_recognizer_impl_unittest.cc b/content/browser/speech/speech_recognizer_impl_unittest.cc index 7168fb6..3e8ef28 100644 --- a/content/browser/speech/speech_recognizer_impl_unittest.cc +++ b/content/browser/speech/speech_recognizer_impl_unittest.cc
@@ -380,7 +380,7 @@ EXPECT_TRUE(recognition_started_); EXPECT_FALSE(audio_started_); EXPECT_FALSE(result_received_); - EXPECT_EQ(SPEECH_RECOGNITION_ERROR_AUDIO, error_); + EXPECT_EQ(SPEECH_RECOGNITION_ERROR_AUDIO_CAPTURE, error_); CheckFinalEventsConsistency(); } @@ -400,7 +400,7 @@ EXPECT_TRUE(recognition_started_); EXPECT_TRUE(audio_started_); EXPECT_FALSE(result_received_); - EXPECT_EQ(SPEECH_RECOGNITION_ERROR_AUDIO, error_); + EXPECT_EQ(SPEECH_RECOGNITION_ERROR_AUDIO_CAPTURE, error_); CheckFinalEventsConsistency(); }
diff --git a/content/browser/ssl/ssl_policy.cc b/content/browser/ssl/ssl_policy.cc index 610f741..5a627fb 100644 --- a/content/browser/ssl/ssl_policy.cc +++ b/content/browser/ssl/ssl_policy.cc
@@ -8,6 +8,7 @@ #include "base/bind.h" #include "base/command_line.h" #include "base/memory/singleton.h" +#include "base/metrics/histogram_macros.h" #include "base/strings/string_piece.h" #include "base/strings/string_util.h" #include "content/browser/frame_host/navigation_entry_impl.h" @@ -26,6 +27,16 @@ namespace content { +namespace { + +// Events for UMA. Do not reorder or change! +enum SSLGoodCertSeenEvent { + NO_PREVIOUS_EXCEPTION = 0, + HAD_PREVIOUS_EXCEPTION = 1, + SSL_GOOD_CERT_SEEN_EVENT_MAX = 2 +}; +} + SSLPolicy::SSLPolicy(SSLPolicyBackend* backend) : backend_(backend) { DCHECK(backend_); @@ -110,8 +121,20 @@ // this information back through WebKit and out some FrameLoaderClient // methods. - if (net::IsCertStatusError(info->ssl_cert_status())) + if (net::IsCertStatusError(info->ssl_cert_status())) { backend_->HostRanInsecureContent(info->url().host(), info->child_id()); + } else { + SSLGoodCertSeenEvent event = NO_PREVIOUS_EXCEPTION; + if (backend_->HasAllowException(info->url().host())) { + // If there's no certificate error, a good certificate has been seen, so + // clear out any exceptions that were made by the user for bad + // certificates. + backend_->RevokeUserAllowExceptions(info->url().host()); + event = HAD_PREVIOUS_EXCEPTION; + } + UMA_HISTOGRAM_ENUMERATION("interstitial.ssl.good_cert_seen", event, + SSL_GOOD_CERT_SEEN_EVENT_MAX); + } } void SSLPolicy::UpdateEntry(NavigationEntryImpl* entry,
diff --git a/content/browser/ssl/ssl_policy_backend.cc b/content/browser/ssl/ssl_policy_backend.cc index 5c65874..a2626da 100644 --- a/content/browser/ssl/ssl_policy_backend.cc +++ b/content/browser/ssl/ssl_policy_backend.cc
@@ -31,6 +31,20 @@ return ssl_host_state_delegate_->DidHostRunInsecureContent(host, pid); } +void SSLPolicyBackend::RevokeUserAllowExceptions(const std::string& host) { + if (!ssl_host_state_delegate_) + return; + + ssl_host_state_delegate_->RevokeUserAllowExceptions(host); +} + +bool SSLPolicyBackend::HasAllowException(const std::string& host) { + if (!ssl_host_state_delegate_) + return false; + + return ssl_host_state_delegate_->HasAllowException(host); +} + void SSLPolicyBackend::AllowCertForHost(const net::X509Certificate& cert, const std::string& host, net::CertStatus error) {
diff --git a/content/browser/ssl/ssl_policy_backend.h b/content/browser/ssl/ssl_policy_backend.h index 15ebe31..ed50c24 100644 --- a/content/browser/ssl/ssl_policy_backend.h +++ b/content/browser/ssl/ssl_policy_backend.h
@@ -27,6 +27,13 @@ // Returns whether the specified host ran insecure content. bool DidHostRunInsecureContent(const std::string& host, int pid) const; + // Revokes all allow exceptions by the user for |host|. + void RevokeUserAllowExceptions(const std::string& host); + + // Returns true if and only if a user exception has previously been made for + // |host|. + bool HasAllowException(const std::string& host); + // Records that |cert| is permitted to be used for |host| in the future, for // a specific error type. void AllowCertForHost(const net::X509Certificate& cert,
diff --git a/content/browser/tracing/tracing_controller_impl.cc b/content/browser/tracing/tracing_controller_impl.cc index 34e384a..68984fa 100644 --- a/content/browser/tracing/tracing_controller_impl.cc +++ b/content/browser/tracing/tracing_controller_impl.cc
@@ -937,6 +937,10 @@ tmf->SendProcessMemoryDumpRequest(args); } +bool TracingControllerImpl::IsCoordinatorProcess() const { + return true; +} + void TracingControllerImpl::OnProcessMemoryDumpResponse( TraceMessageFilter* trace_message_filter, uint64 dump_guid,
diff --git a/content/browser/tracing/tracing_controller_impl.h b/content/browser/tracing/tracing_controller_impl.h index ce504e6..4b732ec 100644 --- a/content/browser/tracing/tracing_controller_impl.h +++ b/content/browser/tracing/tracing_controller_impl.h
@@ -61,6 +61,7 @@ void RequestGlobalMemoryDump( const base::trace_event::MemoryDumpRequestArgs& args, const base::trace_event::MemoryDumpCallback& callback) override; + bool IsCoordinatorProcess() const override; private: typedef std::set<scoped_refptr<TraceMessageFilter> > TraceMessageFilterSet;
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc index 05f9806..3ce4b68 100644 --- a/content/browser/web_contents/web_contents_impl.cc +++ b/content/browser/web_contents/web_contents_impl.cc
@@ -24,7 +24,6 @@ #include "content/browser/browser_plugin/browser_plugin_embedder.h" #include "content/browser/browser_plugin/browser_plugin_guest.h" #include "content/browser/child_process_security_policy_impl.h" -#include "content/browser/devtools/devtools_manager.h" #include "content/browser/dom_storage/dom_storage_context_wrapper.h" #include "content/browser/dom_storage/session_storage_namespace_impl.h" #include "content/browser/download/download_stats.h" @@ -3077,6 +3076,15 @@ void WebContentsImpl::OnUpdateFaviconURL( const std::vector<FaviconURL>& candidates) { + // We get updated favicon URLs after the page stops loading. If a cross-site + // navigation occurs while a page is still loading, the initial page + // may stop loading and send us updated favicon URLs after the navigation + // for the new page has committed. + RenderViewHostImpl* rvhi = + static_cast<RenderViewHostImpl*>(render_view_message_source_); + if (!rvhi->is_active()) + return; + FOR_EACH_OBSERVER(WebContentsObserver, observers_, DidUpdateFaviconURL(candidates)); } @@ -3561,8 +3569,6 @@ FOR_EACH_OBSERVER( WebContentsObserver, observers_, RenderViewCreated(render_view_host)); - - DevToolsManager::GetInstance()->RenderViewCreated(this, render_view_host); } void WebContentsImpl::RenderViewReady(RenderViewHost* rvh) {
diff --git a/content/browser/web_contents/web_contents_impl_unittest.cc b/content/browser/web_contents/web_contents_impl_unittest.cc index 620b194..3a82a88 100644 --- a/content/browser/web_contents/web_contents_impl_unittest.cc +++ b/content/browser/web_contents/web_contents_impl_unittest.cc
@@ -2700,14 +2700,12 @@ EXPECT_EQ(1u, instance->GetRelatedActiveContentsCount()); // Navigate to a URL with WebUI. This will change BrowsingInstances. - contents->GetController().LoadURL(GURL(kTestWebUIUrl), + const GURL kWebUIUrl = GURL(kTestWebUIUrl); + contents->GetController().LoadURL(kWebUIUrl, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string()); - if (base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kEnableBrowserSideNavigation)) { - contents->GetMainFrame()->PrepareForCommit(); - } + contents->GetMainFrame()->PrepareForCommit(); EXPECT_TRUE(contents->CrossProcessNavigationPending()); scoped_refptr<SiteInstance> instance_webui( contents->GetPendingMainFrame()->GetSiteInstance()); @@ -2718,7 +2716,7 @@ EXPECT_EQ(0u, instance_webui->GetRelatedActiveContentsCount()); // Commit and contents counts for the new one. - contents->CommitPendingNavigation(); + contents->GetPendingMainFrame()->SendNavigate(1, kWebUIUrl); EXPECT_EQ(0u, instance->GetRelatedActiveContentsCount()); EXPECT_EQ(1u, instance_webui->GetRelatedActiveContentsCount());
diff --git a/content/browser/web_contents/web_contents_view_mac.mm b/content/browser/web_contents/web_contents_view_mac.mm index 8cf8cd8..9e1f792 100644 --- a/content/browser/web_contents/web_contents_view_mac.mm +++ b/content/browser/web_contents/web_contents_view_mac.mm
@@ -594,8 +594,8 @@ return; NSSelectionDirection direction = - [[[notification userInfo] objectForKey:kSelectionDirection] - unsignedIntegerValue]; + static_cast<NSSelectionDirection>([[[notification userInfo] + objectForKey:kSelectionDirection] unsignedIntegerValue]); if (direction == NSDirectSelection) return;
diff --git a/content/child/BUILD.gn b/content/child/BUILD.gn index fe296dd..77898e7a 100644 --- a/content/child/BUILD.gn +++ b/content/child/BUILD.gn
@@ -110,6 +110,7 @@ sources = [] } else { deps += [ + "//components/scheduler:scheduler", "//content/app/resources", "//content/app/strings", "//crypto:platform",
diff --git a/content/child/DEPS b/content/child/DEPS index 42cba53..7931bc2 100644 --- a/content/child/DEPS +++ b/content/child/DEPS
@@ -1,9 +1,11 @@ include_rules = [ # Allow inclusion of specific components that we depend on. We may only # depend on components which we share with the mojo html_viewer. + "+components/scheduler/child", + "+components/scheduler/common", + "+components/tracing", "+components/webcrypto", - "+components/tracing", "+content/app/strings/grit", # For generated headers "+content/public/child", "+media/base/android",
diff --git a/content/child/blink_platform_impl.cc b/content/child/blink_platform_impl.cc index 8a58c91..25faab4 100644 --- a/content/child/blink_platform_impl.cc +++ b/content/child/blink_platform_impl.cc
@@ -27,6 +27,7 @@ #include "base/time/time.h" #include "blink/public/resources/grit/blink_image_resources.h" #include "blink/public/resources/grit/blink_resources.h" +#include "components/scheduler/child/webthread_impl_for_worker_scheduler.h" #include "content/app/resources/grit/content_resources.h" #include "content/app/strings/grit/content_strings.h" #include "content/child/bluetooth/web_bluetooth_impl.h" @@ -40,7 +41,6 @@ #include "content/child/permissions/permission_dispatcher_thread_proxy.h" #include "content/child/push_messaging/push_dispatcher.h" #include "content/child/push_messaging/push_provider.h" -#include "content/child/scheduler/webthread_impl_for_worker_scheduler.h" #include "content/child/thread_safe_sender.h" #include "content/child/web_discardable_memory_impl.h" #include "content/child/web_url_loader_impl.h" @@ -519,8 +519,8 @@ } blink::WebThread* BlinkPlatformImpl::createThread(const char* name) { - WebThreadImplForWorkerScheduler* thread = - new WebThreadImplForWorkerScheduler(name); + scheduler::WebThreadImplForWorkerScheduler* thread = + new scheduler::WebThreadImplForWorkerScheduler(name); thread->TaskRunner()->PostTask( FROM_HERE, base::Bind(&BlinkPlatformImpl::UpdateWebThreadTLS, base::Unretained(this), thread)); @@ -589,15 +589,18 @@ return TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED(category_group); } -long* BlinkPlatformImpl::getTraceSamplingState( - const unsigned thread_bucket) { +blink::Platform::TraceEventAPIAtomicWord* +BlinkPlatformImpl::getTraceSamplingState(const unsigned thread_bucket) { switch (thread_bucket) { case 0: - return reinterpret_cast<long*>(&TRACE_EVENT_API_THREAD_BUCKET(0)); + return reinterpret_cast<blink::Platform::TraceEventAPIAtomicWord*>( + &TRACE_EVENT_API_THREAD_BUCKET(0)); case 1: - return reinterpret_cast<long*>(&TRACE_EVENT_API_THREAD_BUCKET(1)); + return reinterpret_cast<blink::Platform::TraceEventAPIAtomicWord*>( + &TRACE_EVENT_API_THREAD_BUCKET(1)); case 2: - return reinterpret_cast<long*>(&TRACE_EVENT_API_THREAD_BUCKET(2)); + return reinterpret_cast<blink::Platform::TraceEventAPIAtomicWord*>( + &TRACE_EVENT_API_THREAD_BUCKET(2)); default: NOTREACHED() << "Unknown thread bucket type."; }
diff --git a/content/child/blink_platform_impl.h b/content/child/blink_platform_impl.h index 7ae04dc..3b5462d2 100644 --- a/content/child/blink_platform_impl.h +++ b/content/child/blink_platform_impl.h
@@ -102,7 +102,8 @@ virtual void histogramSparse(const char* name, int sample); virtual const unsigned char* getTraceCategoryEnabledFlag( const char* category_name); - virtual long* getTraceSamplingState(const unsigned thread_bucket); + virtual TraceEventAPIAtomicWord* getTraceSamplingState( + const unsigned thread_bucket); virtual TraceEventHandle addTraceEvent( char phase, const unsigned char* category_group_enabled,
diff --git a/content/child/child_thread_impl.cc b/content/child/child_thread_impl.cc index 30dbbe89..d2f1791b 100644 --- a/content/child/child_thread_impl.cc +++ b/content/child/child_thread_impl.cc
@@ -13,6 +13,7 @@ #include "base/basictypes.h" #include "base/command_line.h" #include "base/debug/leak_annotations.h" +#include "base/debug/profiler.h" #include "base/lazy_instance.h" #include "base/logging.h" #include "base/message_loop/message_loop.h" @@ -25,6 +26,7 @@ #include "base/synchronization/condition_variable.h" #include "base/synchronization/lock.h" #include "base/threading/thread_local.h" +#include "base/trace_event/memory_dump_manager.h" #include "base/tracked_objects.h" #include "components/tracing/child_trace_message_filter.h" #include "content/child/bluetooth/bluetooth_message_filter.h" @@ -138,6 +140,7 @@ // // So, we install a filter on the sender so that we can process this event // here and kill the process. + base::debug::StopProfiling(); #if defined(ADDRESS_SANITIZER) || defined(LEAK_SANITIZER) || \ defined(MEMORY_SANITIZER) || defined(THREAD_SANITIZER) || \ defined(UNDEFINED_SANITIZER) @@ -419,6 +422,8 @@ ::HeapProfilerStop, ::GetHeapProfile)); #endif + base::trace_event::MemoryDumpManager::GetInstance()->Initialize(); + shared_bitmap_manager_.reset( new ChildSharedBitmapManager(thread_safe_sender()));
diff --git a/content/child/runtime_features.cc b/content/child/runtime_features.cc index 39adb12..b4d5df5 100644 --- a/content/child/runtime_features.cc +++ b/content/child/runtime_features.cc
@@ -9,6 +9,7 @@ #include "base/command_line.h" #include "base/metrics/field_trial.h" #include "base/strings/string_split.h" +#include "components/scheduler/common/scheduler_switches.h" #include "content/common/content_switches_internal.h" #include "content/public/common/content_switches.h" #include "third_party/WebKit/public/web/WebRuntimeFeatures.h" @@ -96,7 +97,7 @@ if (command_line.HasSwitch(switches::kDisableDatabases)) WebRuntimeFeatures::enableDatabase(false); - if (command_line.HasSwitch(switches::kDisableBlinkScheduler)) + if (command_line.HasSwitch(scheduler::switches::kDisableBlinkScheduler)) WebRuntimeFeatures::enableBlinkScheduler(false); if (command_line.HasSwitch(switches::kDisableLocalStorage)) @@ -116,9 +117,6 @@ WebRuntimeFeatures::enableSharedWorker(false); #if defined(OS_ANDROID) - if (command_line.HasSwitch(switches::kDisableWebRTC)) - WebRuntimeFeatures::enablePeerConnection(false); - // WebAudio is enabled by default on ARM and X86, if the MediaCodec // API is available. WebRuntimeFeatures::enableWebAudio(
diff --git a/content/child/scheduler/null_idle_task_runner.h b/content/child/scheduler/null_idle_task_runner.h deleted file mode 100644 index 31a83fc..0000000 --- a/content/child/scheduler/null_idle_task_runner.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 CONTENT_CHILD_SCHEDULER_NULL_IDLE_TASK_RUNNER_H_ -#define CONTENT_CHILD_SCHEDULER_NULL_IDLE_TASK_RUNNER_H_ - -#include "content/child/scheduler/single_thread_idle_task_runner.h" - -namespace content { - -class NullIdleTaskRunner : public SingleThreadIdleTaskRunner { - public: - NullIdleTaskRunner(); - void PostIdleTask(const tracked_objects::Location& from_here, - const IdleTask& idle_task) override; - - void PostNonNestableIdleTask( - const tracked_objects::Location& from_here, - const IdleTask& idle_task) override; - - void PostIdleTaskAfterWakeup( - const tracked_objects::Location& from_here, - const IdleTask& idle_task) override; - - protected: - ~NullIdleTaskRunner() override; - - private: - DISALLOW_COPY_AND_ASSIGN(NullIdleTaskRunner); -}; - -} // namespace content - -#endif // CONTENT_CHILD_SCHEDULER_NULL_IDLE_TASK_RUNNER_H_
diff --git a/content/child/scheduler/time_source.h b/content/child/scheduler/time_source.h deleted file mode 100644 index e5d8606b..0000000 --- a/content/child/scheduler/time_source.h +++ /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. - -#ifndef CONTENT_CHILD_SCHEDULER_TIME_SOURCE_H_ -#define CONTENT_CHILD_SCHEDULER_TIME_SOURCE_H_ - -#include "base/time/time.h" -#include "content/common/content_export.h" - -namespace content { - -class CONTENT_EXPORT TimeSource { - public: - TimeSource(); - virtual ~TimeSource(); - - virtual base::TimeTicks Now() const; - - private: - DISALLOW_COPY_AND_ASSIGN(TimeSource); -}; - -} // namespace content - -#endif // CONTENT_CHILD_SCHEDULER_TIME_SOURCE_H_
diff --git a/content/child/scheduler/worker_scheduler.h b/content/child/scheduler/worker_scheduler.h deleted file mode 100644 index 7d12251..0000000 --- a/content/child/scheduler/worker_scheduler.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 CONTENT_CHILD_SCHEDULER_WORKER_SCHEDULER_H_ -#define CONTENT_CHILD_SCHEDULER_WORKER_SCHEDULER_H_ - -#include "base/message_loop/message_loop.h" -#include "content/child/scheduler/child_scheduler.h" -#include "content/child/scheduler/single_thread_idle_task_runner.h" -#include "content/common/content_export.h" - -namespace base { -class MessageLoop; -} - -namespace content { - -class CONTENT_EXPORT WorkerScheduler : public ChildScheduler { - public: - ~WorkerScheduler() override; - static scoped_ptr<WorkerScheduler> Create(base::MessageLoop* message_loop); - - // Must be called before the scheduler can be used. Does any post construction - // initialization needed such as initializing idle period detection. - virtual void Init() = 0; - - protected: - WorkerScheduler(); - DISALLOW_COPY_AND_ASSIGN(WorkerScheduler); -}; - -} // namespace content - -#endif // CONTENT_CHILD_SCHEDULER_WORKER_SCHEDULER_H_
diff --git a/content/child/threaded_data_provider.cc b/content/child/threaded_data_provider.cc index 395e452..eea420a 100644 --- a/content/child/threaded_data_provider.cc +++ b/content/child/threaded_data_provider.cc
@@ -4,10 +4,10 @@ #include "content/child/threaded_data_provider.h" +#include "components/scheduler/child/webthread_impl_for_worker_scheduler.h" #include "content/child/child_process.h" #include "content/child/child_thread_impl.h" #include "content/child/resource_dispatcher.h" -#include "content/child/scheduler/webthread_impl_for_worker_scheduler.h" #include "content/child/thread_safe_sender.h" #include "content/common/resource_messages.h" #include "ipc/ipc_sync_channel.h" @@ -23,7 +23,7 @@ DataProviderMessageFilter( const scoped_refptr<base::MessageLoopProxy>& io_message_loop, scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner, - const WebThreadImplForWorkerScheduler& background_thread, + const scheduler::WebThreadImplForWorkerScheduler& background_thread, const base::WeakPtr<ThreadedDataProvider>& background_thread_resource_provider, const base::WeakPtr<ThreadedDataProvider>& main_thread_resource_provider, @@ -41,7 +41,7 @@ const scoped_refptr<base::MessageLoopProxy> io_message_loop_; scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner_; - const WebThreadImplForWorkerScheduler& background_thread_; + const scheduler::WebThreadImplForWorkerScheduler& background_thread_; // This weakptr can only be dereferenced on the background thread. base::WeakPtr<ThreadedDataProvider> background_thread_resource_provider_; @@ -54,7 +54,7 @@ DataProviderMessageFilter::DataProviderMessageFilter( const scoped_refptr<base::MessageLoopProxy>& io_message_loop, scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner, - const WebThreadImplForWorkerScheduler& background_thread, + const scheduler::WebThreadImplForWorkerScheduler& background_thread, const base::WeakPtr<ThreadedDataProvider>& background_thread_resource_provider, const base::WeakPtr<ThreadedDataProvider>& main_thread_resource_provider, @@ -126,8 +126,9 @@ : request_id_(request_id), shm_buffer_(shm_buffer), shm_size_(shm_size), - background_thread_(static_cast<WebThreadImplForWorkerScheduler&>( - *threaded_data_receiver->backgroundThread())), + background_thread_( + static_cast<scheduler::WebThreadImplForWorkerScheduler&>( + *threaded_data_receiver->backgroundThread())), ipc_channel_(ChildThreadImpl::current()->channel()), threaded_data_receiver_(threaded_data_receiver), resource_filter_active_(false), @@ -186,7 +187,8 @@ // We should never end up with a different parser thread than from when the // ThreadedDataProvider gets created. DCHECK(current_background_thread == - static_cast<WebThreadImplForWorkerScheduler*>(&background_thread_)); + static_cast<scheduler::WebThreadImplForWorkerScheduler*>( + &background_thread_)); background_thread_.TaskRunner()->PostTask( FROM_HERE, base::Bind(&ThreadedDataProvider::StopOnBackgroundThread, base::Unretained(this)));
diff --git a/content/child/threaded_data_provider.h b/content/child/threaded_data_provider.h index effb4f9..5cd74330 100644 --- a/content/child/threaded_data_provider.h +++ b/content/child/threaded_data_provider.h
@@ -24,9 +24,12 @@ class SyncChannel; } +namespace scheduler { +class WebThreadImplForWorkerScheduler; +} + namespace content { class ResourceDispatcher; -class WebThreadImplForWorkerScheduler; class ThreadedDataProvider { public: @@ -78,7 +81,7 @@ int shm_size_; scoped_ptr<base::WeakPtrFactory<ThreadedDataProvider> > background_thread_weak_factory_; - WebThreadImplForWorkerScheduler& background_thread_; + scheduler::WebThreadImplForWorkerScheduler& background_thread_; IPC::SyncChannel* ipc_channel_; blink::WebThreadedDataReceiver* threaded_data_receiver_; bool resource_filter_active_;
diff --git a/content/child/web_url_loader_impl.cc b/content/child/web_url_loader_impl.cc index c3431b5..a1a5beb4 100644 --- a/content/child/web_url_loader_impl.cc +++ b/content/child/web_url_loader_impl.cc
@@ -778,6 +778,11 @@ if (request_.downloadToFile()) return false; + // Data url requests from object tags may need to be intercepted as streams + // and so need to be sent to the browser. + if (request_.requestContext() == WebURLRequest::RequestContextObject) + return false; + // Optimize for the case where we can handle a data URL locally. We must // skip this for data URLs targetted at frames since those could trigger a // download.
diff --git a/content/common/gpu/media/h264_decoder.cc b/content/common/gpu/media/h264_decoder.cc index 02ba468c..9953ff2 100644 --- a/content/common/gpu/media/h264_decoder.cc +++ b/content/common/gpu/media/h264_decoder.cc
@@ -615,6 +615,9 @@ void H264Decoder::OutputPic(scoped_refptr<H264Picture> pic) { DCHECK(!pic->outputted); pic->outputted = true; + + DVLOG_IF(1, pic->pic_order_cnt < last_output_poc_) + << "Outputting out of order, likely a broken stream"; last_output_poc_ = pic->pic_order_cnt; DVLOG(4) << "Posting output task for POC: " << pic->pic_order_cnt; @@ -895,16 +898,25 @@ // to remain in the DPB and can be removed. H264Picture::Vector::iterator output_candidate = not_outputted.begin(); size_t num_remaining = not_outputted.size(); - while (num_remaining > max_num_reorder_frames_) { - int poc = (*output_candidate)->pic_order_cnt; - DCHECK_GE(poc, last_output_poc_); + while (num_remaining > max_num_reorder_frames_ || + // If the condition below is used, this is an invalid stream. We should + // not be forced to output beyond max_num_reorder_frames in order to + // make room in DPB to store the current picture (if we need to do so). + // However, if this happens, ignore max_num_reorder_frames and try + // to output more. This may cause out-of-order output, but is not + // fatal, and better than failing instead. + ((dpb_.IsFull() && (!pic->outputted || pic->ref)) && num_remaining)) { + DVLOG_IF(1, num_remaining <= max_num_reorder_frames_) + << "Invalid stream: max_num_reorder_frames not preserved"; + OutputPic(*output_candidate); if (!(*output_candidate)->ref) { // Current picture hasn't been inserted into DPB yet, so don't remove it // if we managed to output it immediately. - if ((*output_candidate)->pic_order_cnt != pic->pic_order_cnt) - dpb_.DeleteByPOC(poc); + int outputted_poc = (*output_candidate)->pic_order_cnt; + if (outputted_poc != pic->pic_order_cnt) + dpb_.DeleteByPOC(outputted_poc); } ++output_candidate;
diff --git a/content/common/gpu/media/v4l2_video_encode_accelerator.cc b/content/common/gpu/media/v4l2_video_encode_accelerator.cc index d075740..e2d29c5 100644 --- a/content/common/gpu/media/v4l2_video_encode_accelerator.cc +++ b/content/common/gpu/media/v4l2_video_encode_accelerator.cc
@@ -868,7 +868,6 @@ // Device might have adjusted the required output size. size_t adjusted_output_buffer_size = base::checked_cast<size_t>(format.fmt.pix_mp.plane_fmt[0].sizeimage); - DCHECK_GE(adjusted_output_buffer_size, output_buffer_byte_size_); output_buffer_byte_size_ = adjusted_output_buffer_size; return true;
diff --git a/content/content_browser.gypi b/content/content_browser.gypi index 2eece7a..87c7a71a 100644 --- a/content/content_browser.gypi +++ b/content/content_browser.gypi
@@ -1729,6 +1729,7 @@ 'browser/devtools/devtools.gyp:devtools_protocol_handler', '../cc/cc.gyp:cc', '../cc/cc.gyp:cc_surfaces', + '../components/scheduler/scheduler.gyp:scheduler_common', '../device/bluetooth/bluetooth.gyp:device_bluetooth', '../net/net.gyp:http_server', '../storage/storage_browser.gyp:storage', @@ -1815,12 +1816,6 @@ 'browser/media/capture/desktop_capture_device_aura.h', ], }], - ['enable_webrtc==1 and (OS=="linux" or OS=="mac")', { - 'sources': [ - 'browser/renderer_host/media/video_capture_texture_wrapper.cc', - 'browser/renderer_host/media/video_capture_texture_wrapper.h', - ], - }], ['OS=="win"', { 'dependencies': [ # For accessibility
diff --git a/content/content_child.gypi b/content/content_child.gypi index 297c40d..02da309 100644 --- a/content/content_child.gypi +++ b/content/content_child.gypi
@@ -190,35 +190,6 @@ 'child/request_extra_data.h', 'child/request_info.cc', 'child/request_info.h', - 'child/scheduler/cancelable_closure_holder.cc', - 'child/scheduler/cancelable_closure_holder.h', - 'child/scheduler/child_scheduler.h', - 'child/scheduler/nestable_single_thread_task_runner.h', - 'child/scheduler/null_idle_task_runner.cc', - 'child/scheduler/null_idle_task_runner.h', - 'child/scheduler/null_worker_scheduler.cc', - 'child/scheduler/null_worker_scheduler.h', - 'child/scheduler/prioritizing_task_queue_selector.cc', - 'child/scheduler/prioritizing_task_queue_selector.h', - 'child/scheduler/scheduler_helper.cc', - 'child/scheduler/scheduler_helper.h', - 'child/scheduler/scheduler_message_loop_delegate.cc', - 'child/scheduler/scheduler_message_loop_delegate.h', - 'child/scheduler/single_thread_idle_task_runner.cc', - 'child/scheduler/single_thread_idle_task_runner.h', - 'child/scheduler/task_queue_selector.h', - 'child/scheduler/task_queue_manager.cc', - 'child/scheduler/task_queue_manager.h', - 'child/scheduler/time_source.cc', - 'child/scheduler/time_source.h', - 'child/scheduler/webthread_impl_for_worker_scheduler.cc', - 'child/scheduler/webthread_impl_for_worker_scheduler.h', - 'child/scheduler/web_scheduler_impl.cc', - 'child/scheduler/web_scheduler_impl.h', - 'child/scheduler/worker_scheduler.cc', - 'child/scheduler/worker_scheduler.h', - 'child/scheduler/worker_scheduler_impl.cc', - 'child/scheduler/worker_scheduler_impl.h', 'child/resource_dispatcher.cc', 'child/resource_dispatcher.h', 'child/resource_scheduling_filter.cc', @@ -286,8 +257,6 @@ 'child/webthemeengine_impl_default.cc', 'child/webthemeengine_impl_default.h', 'child/webthemeengine_impl_mac.h', - 'child/webthread_base.cc', - 'child/webthread_base.h', 'child/weburlresponse_extradata_impl.cc', 'child/weburlresponse_extradata_impl.h', 'child/worker_task_runner.cc', @@ -333,6 +302,7 @@ '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',
diff --git a/content/content_nacl_nonsfi.gyp b/content/content_nacl_nonsfi.gyp index 4f30ca4..f60f120 100644 --- a/content/content_nacl_nonsfi.gyp +++ b/content/content_nacl_nonsfi.gyp
@@ -32,7 +32,6 @@ }, 'dependencies': [ '../base/base_nacl.gyp:base_nacl_nonsfi', - '../native_client/tools.gyp:prep_toolchain', ], }, ],
diff --git a/content/content_renderer.gypi b/content/content_renderer.gypi index 0ae7fbba..fb2a7cf 100644 --- a/content/content_renderer.gypi +++ b/content/content_renderer.gypi
@@ -8,6 +8,7 @@ '../base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations', '../cc/cc.gyp:cc', '../cc/blink/cc_blink.gyp:cc_blink', + '../components/scheduler/scheduler.gyp:scheduler', '../device/battery/battery.gyp:device_battery', '../device/battery/battery.gyp:device_battery_mojo_bindings', '../device/vibration/vibration.gyp:device_vibration', @@ -371,18 +372,8 @@ 'renderer/sad_plugin.h', 'renderer/savable_resources.cc', 'renderer/savable_resources.h', - 'renderer/scheduler/deadline_task_runner.cc', - 'renderer/scheduler/deadline_task_runner.h', - 'renderer/scheduler/null_renderer_scheduler.cc', - 'renderer/scheduler/null_renderer_scheduler.h', - 'renderer/scheduler/renderer_scheduler.cc', - 'renderer/scheduler/renderer_scheduler.h', - 'renderer/scheduler/renderer_scheduler_impl.cc', - 'renderer/scheduler/renderer_scheduler_impl.h', 'renderer/scheduler/resource_dispatch_throttler.cc', 'renderer/scheduler/resource_dispatch_throttler.h', - 'renderer/scheduler/webthread_impl_for_renderer_scheduler.cc', - 'renderer/scheduler/webthread_impl_for_renderer_scheduler.h', 'renderer/screen_orientation/screen_orientation_dispatcher.cc', 'renderer/screen_orientation/screen_orientation_dispatcher.h', 'renderer/screen_orientation/screen_orientation_observer.cc',
diff --git a/content/content_shell.gypi b/content/content_shell.gypi index 4931f11..20835bd4 100644 --- a/content/content_shell.gypi +++ b/content/content_shell.gypi
@@ -47,6 +47,7 @@ '../cc/blink/cc_blink.gyp:cc_blink', '../cc/cc.gyp:cc', '../components/components.gyp:crash_component_breakpad_mac_to_be_deleted', + '../components/components.gyp:devtools_discovery', '../components/components.gyp:devtools_http_handler', '../components/components.gyp:web_cache_renderer', '../device/bluetooth/bluetooth.gyp:device_bluetooth',
diff --git a/content/content_tests.gypi b/content/content_tests.gypi index a9938c21..660e674 100644 --- a/content/content_tests.gypi +++ b/content/content_tests.gypi
@@ -163,8 +163,6 @@ 'test/test_render_view_host.h', 'test/test_render_view_host_factory.cc', 'test/test_render_view_host_factory.h', - 'test/test_time_source.cc', - 'test/test_time_source.h', 'test/test_web_contents.cc', 'test/test_web_contents.h', 'test/test_web_contents_factory.cc', @@ -190,8 +188,8 @@ 'browser/accessibility/dump_accessibility_browsertest_base.h', 'browser/accessibility/dump_accessibility_events_browsertest.cc', 'browser/accessibility/dump_accessibility_tree_browsertest.cc', - 'browser/accessibility/snapshot_ax_tree_browsertest.cc', 'browser/accessibility/site_per_process_accessibility_browsertest.cc', + 'browser/accessibility/snapshot_ax_tree_browsertest.cc', 'browser/battery_status/battery_monitor_impl_browsertest.cc', 'browser/battery_status/battery_monitor_integration_browsertest.cc', 'browser/bookmarklet_browsertest.cc', @@ -249,8 +247,8 @@ 'browser/web_contents/web_contents_view_aura_browsertest.cc', 'browser/webkit_browsertest.cc', 'browser/webui/web_ui_mojo_browsertest.cc', - 'child/site_isolation_policy_browsertest.cc', 'child/child_discardable_shared_memory_manager_browsertest.cc', + 'child/site_isolation_policy_browsertest.cc', 'renderer/accessibility/renderer_accessibility_browsertest.cc', 'renderer/devtools/v8_sampling_profiler_browsertest.cc', 'renderer/gin_browsertest.cc', @@ -478,10 +476,10 @@ 'browser/media/midi_host_unittest.cc', 'browser/media/webrtc_identity_store_unittest.cc', 'browser/net/sqlite_persistent_cookie_store_unittest.cc', + 'browser/notification_service_impl_unittest.cc', 'browser/notifications/notification_database_data_unittest.cc', 'browser/notifications/notification_database_unittest.cc', 'browser/notifications/platform_notification_context_unittest.cc', - 'browser/notification_service_impl_unittest.cc', 'browser/power_monitor_message_broadcaster_unittest.cc', 'browser/power_profiler/power_profiler_service_unittest.cc', 'browser/power_usage_monitor_impl_unittest.cc', @@ -587,13 +585,6 @@ 'child/notifications/notification_data_conversions_unittest.cc', 'child/power_monitor_broadcast_source_unittest.cc', 'child/resource_dispatcher_unittest.cc', - 'child/scheduler/nestable_task_runner_for_test.cc', - 'child/scheduler/nestable_task_runner_for_test.h', - 'child/scheduler/prioritizing_task_queue_selector_unittest.cc', - 'child/scheduler/scheduler_helper_unittest.cc', - 'child/scheduler/task_queue_manager_unittest.cc', - 'child/scheduler/webthread_impl_for_worker_scheduler_unittest.cc', - 'child/scheduler/worker_scheduler_impl_unittest.cc', 'child/service_worker/service_worker_dispatcher_unittest.cc', 'child/simple_webmimeregistry_impl_unittest.cc', 'child/site_isolation_policy_unittest.cc', @@ -665,10 +656,7 @@ 'renderer/media/video_capture_message_filter_unittest.cc', 'renderer/render_thread_impl_unittest.cc', 'renderer/render_widget_unittest.cc', - 'renderer/scheduler/deadline_task_runner_unittest.cc', - 'renderer/scheduler/renderer_scheduler_impl_unittest.cc', 'renderer/scheduler/resource_dispatch_throttler_unittest.cc', - 'renderer/scheduler/webthread_impl_for_renderer_scheduler_unittest.cc', 'renderer/screen_orientation/screen_orientation_dispatcher_unittest.cc', 'renderer/skia_benchmarking_extension_unittest.cc', 'test/fileapi_test_file_set.cc', @@ -873,6 +861,7 @@ '../cc/blink/cc_blink.gyp:cc_blink', '../cc/cc.gyp:cc', '../cc/cc_tests.gyp:cc_test_support', + '../components/scheduler/scheduler.gyp:scheduler', '../gpu/blink/gpu_blink.gyp:gpu_blink', '../ipc/mojo/ipc_mojo.gyp:*', '../media/blink/media_blink.gyp:media_blink', @@ -1256,7 +1245,6 @@ 'browser/renderer_host/input/input_router_impl_perftest.cc', 'common/cc_messages_perftest.cc', 'common/discardable_shared_memory_heap_perftest.cc', - 'child/scheduler/task_queue_manager_perftest.cc', 'test/run_all_perftests.cc', ], 'conditions': [
diff --git a/content/public/android/java/src/org/chromium/content/browser/ContentView.java b/content/public/android/java/src/org/chromium/content/browser/ContentView.java index 63761a69..33a7c2c 100644 --- a/content/public/android/java/src/org/chromium/content/browser/ContentView.java +++ b/content/public/android/java/src/org/chromium/content/browser/ContentView.java
@@ -8,7 +8,6 @@ import android.content.res.Configuration; import android.graphics.Canvas; import android.graphics.Rect; -import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.Message; @@ -16,8 +15,7 @@ import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; -import android.view.accessibility.AccessibilityEvent; -import android.view.accessibility.AccessibilityNodeInfo; +import android.view.accessibility.AccessibilityNodeProvider; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputConnection; import android.widget.FrameLayout; @@ -40,17 +38,8 @@ * @param context The Context the view is running in, through which it can * access the current theme, resources, etc. * @param cvc A pointer to the content view core managing this content view. - * @return A ContentView instance. */ - public static ContentView newInstance(Context context, ContentViewCore cvc) { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) { - return new ContentView(context, cvc); - } else { - return new JellyBeanContentView(context, cvc); - } - } - - protected ContentView(Context context, ContentViewCore cvc) { + public ContentView(Context context, ContentViewCore cvc) { super(context, null, android.R.attr.webViewStyle); if (getScrollBarStyle() == View.SCROLLBARS_INSIDE_OVERLAY) { @@ -64,6 +53,25 @@ mContentViewCore = cvc; } + @Override + public boolean performAccessibilityAction(int action, Bundle arguments) { + if (mContentViewCore.supportsAccessibilityAction(action)) { + return mContentViewCore.performAccessibilityAction(action, arguments); + } + + return super.performAccessibilityAction(action, arguments); + } + + @Override + public AccessibilityNodeProvider getAccessibilityNodeProvider() { + AccessibilityNodeProvider provider = mContentViewCore.getAccessibilityNodeProvider(); + if (provider != null) { + return provider; + } else { + return super.getAccessibilityNodeProvider(); + } + } + // Needed by ContentViewCore.InternalAccessDelegate @Override public boolean drawChild(Canvas canvas, View child, long drawingTime) { @@ -226,22 +234,6 @@ } @Override - public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { - super.onInitializeAccessibilityNodeInfo(info); - mContentViewCore.onInitializeAccessibilityNodeInfo(info); - } - - /** - * Fills in scrolling values for AccessibilityEvents. - * @param event Event being fired. - */ - @Override - public void onInitializeAccessibilityEvent(AccessibilityEvent event) { - super.onInitializeAccessibilityEvent(event); - mContentViewCore.onInitializeAccessibilityEvent(event); - } - - @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); mContentViewCore.onAttachedToWindow(); @@ -273,6 +265,7 @@ return; } mContentViewCore.setSmartClipDataListener(new ContentViewCore.SmartClipDataListener() { + @Override public void onSmartClipDataExtracted(String text, String html, Rect clipRect) { Bundle bundle = new Bundle(); bundle.putString("url", mContentViewCore.getWebContents().getVisibleUrl());
diff --git a/content/public/android/java/src/org/chromium/content/browser/ContentViewClient.java b/content/public/android/java/src/org/chromium/content/browser/ContentViewClient.java index 8775c6bb..a874357 100644 --- a/content/public/android/java/src/org/chromium/content/browser/ContentViewClient.java +++ b/content/public/android/java/src/org/chromium/content/browser/ContentViewClient.java
@@ -171,15 +171,6 @@ } /** - * @return Whether javascript is enabled by the embedder. - */ - // TODO(tedchoc): Only used for ICS accessibility injection, so remove this method when - // that is no longer needed. - public boolean isJavascriptEnabled() { - return true; - } - - /** * @return Whether an externally managed (i.e., not compositor-driven) fling * of this ContentView is active. */
diff --git a/content/public/android/java/src/org/chromium/content/browser/ContentViewCore.java b/content/public/android/java/src/org/chromium/content/browser/ContentViewCore.java index cb233d4..c064988 100644 --- a/content/public/android/java/src/org/chromium/content/browser/ContentViewCore.java +++ b/content/public/android/java/src/org/chromium/content/browser/ContentViewCore.java
@@ -9,7 +9,6 @@ import android.app.Activity; import android.app.SearchManager; import android.content.ClipboardManager; -import android.content.ContentResolver; import android.content.Context; import android.content.ContextWrapper; import android.content.Intent; @@ -19,14 +18,12 @@ import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Rect; -import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.ResultReceiver; import android.os.SystemClock; import android.provider.Browser; -import android.provider.Settings; import android.text.Editable; import android.text.Selection; import android.text.TextUtils; @@ -38,10 +35,8 @@ import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; -import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener; -import android.view.accessibility.AccessibilityNodeInfo; import android.view.accessibility.AccessibilityNodeProvider; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputConnection; @@ -58,7 +53,6 @@ import org.chromium.base.VisibleForTesting; import org.chromium.content.R; import org.chromium.content.browser.ScreenOrientationListener.ScreenOrientationObserver; -import org.chromium.content.browser.accessibility.AccessibilityInjector; import org.chromium.content.browser.accessibility.BrowserAccessibilityManager; import org.chromium.content.browser.accessibility.captioning.CaptioningBridgeFactory; import org.chromium.content.browser.accessibility.captioning.SystemCaptioningBridge; @@ -89,7 +83,6 @@ import java.lang.annotation.Annotation; import java.lang.ref.WeakReference; -import java.lang.reflect.Field; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; @@ -314,20 +307,6 @@ } @Override - public void didStartLoading(String url) { - ContentViewCore contentViewCore = mWeakContentViewCore.get(); - if (contentViewCore == null) return; - contentViewCore.mAccessibilityInjector.onPageLoadStarted(); - } - - @Override - public void didStopLoading(String url) { - ContentViewCore contentViewCore = mWeakContentViewCore.get(); - if (contentViewCore == null) return; - contentViewCore.mAccessibilityInjector.onPageLoadStopped(); - } - - @Override public void didFailLoad(boolean isProvisionalLoad, boolean isMainFrame, int errorCode, String description, String failingUrl) { // Navigation that fails the provisional load will have the strong binding removed @@ -539,9 +518,6 @@ // Delegate that will handle GET downloads, and be notified of completion of POST downloads. private ContentViewDownloadDelegate mDownloadDelegate; - // The AccessibilityInjector that handles loading Accessibility scripts into the web page. - private AccessibilityInjector mAccessibilityInjector; - // Whether native accessibility, i.e. without any script injection, is allowed. private boolean mNativeAccessibilityAllowed; @@ -837,8 +813,6 @@ mImeAdapter = createImeAdapter(); attachImeAdapter(); - mAccessibilityInjector = AccessibilityInjector.newInstance(this); - mWebContentsObserver = new ContentViewWebContentsObserver(this); } @@ -989,7 +963,6 @@ if (mNativeContentViewCore != 0) { nativeOnJavaContentViewCoreDestroyed(mNativeContentViewCore); } - mSystemCaptioningBridge.destroy(); mWebContentsObserver.destroy(); mWebContentsObserver = null; setSmartClipDataListener(null); @@ -1443,7 +1416,6 @@ public void onHide() { assert mWebContents != null; hidePopupsAndPreserveSelection(); - setInjectedAccessibility(false); mWebContents.onHide(); } @@ -1506,6 +1478,7 @@ restoreSelectionPopupsIfNecessary(); ScreenOrientationListener.getInstance().addObserver(this, mContext); GamepadList.onAttachedToWindow(mContext); + mSystemCaptioningBridge.registerBridge(); } /** @@ -1514,7 +1487,6 @@ @SuppressWarnings("javadoc") @SuppressLint("MissingSuperCall") public void onDetachedFromWindow() { - setInjectedAccessibility(false); mZoomControlsDelegate.dismissZoomPicker(); unregisterAccessibilityContentObserver(); @@ -1528,6 +1500,7 @@ // locking and app switching. setTextHandlesTemporarilyHidden(true); hidePopupsAndPreserveSelection(); + mSystemCaptioningBridge.unregisterBridge(); } /** @@ -2877,7 +2850,8 @@ * @return Whether or not this action is supported. */ public boolean supportsAccessibilityAction(int action) { - return mAccessibilityInjector.supportsAccessibilityAction(action); + // TODO(dmazzoni): implement this in BrowserAccessibilityManager. + return false; } /** @@ -2891,10 +2865,7 @@ * the super {@link View} class. */ public boolean performAccessibilityAction(int action, Bundle arguments) { - if (mAccessibilityInjector.supportsAccessibilityAction(action)) { - return mAccessibilityInjector.performAccessibilityAction(action, arguments); - } - + // TODO(dmazzoni): implement this in BrowserAccessibilityManager. return false; } @@ -2955,96 +2926,6 @@ } /** - * @see View#onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo) - */ - public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { - // Note: this is only used by the script-injecting accessibility code. - mAccessibilityInjector.onInitializeAccessibilityNodeInfo(info); - } - - /** - * @see View#onInitializeAccessibilityEvent(AccessibilityEvent) - */ - @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) - public void onInitializeAccessibilityEvent(AccessibilityEvent event) { - // Note: this is only used by the script-injecting accessibility code. - event.setClassName(this.getClass().getName()); - - // Identify where the top-left of the screen currently points to. - event.setScrollX(mRenderCoordinates.getScrollXPixInt()); - event.setScrollY(mRenderCoordinates.getScrollYPixInt()); - - // The maximum scroll values are determined by taking the content dimensions and - // subtracting off the actual dimensions of the ChromeView. - int maxScrollXPix = Math.max(0, mRenderCoordinates.getMaxHorizontalScrollPixInt()); - int maxScrollYPix = Math.max(0, mRenderCoordinates.getMaxVerticalScrollPixInt()); - event.setScrollable(maxScrollXPix > 0 || maxScrollYPix > 0); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) { - event.setMaxScrollX(maxScrollXPix); - event.setMaxScrollY(maxScrollYPix); - } - } - - /** - * Returns whether accessibility script injection is enabled on the device - */ - public boolean isDeviceAccessibilityScriptInjectionEnabled() { - try { - // On JellyBean and higher, native accessibility is the default so script - // injection is only allowed if enabled via a flag. - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN - && !CommandLine.getInstance().hasSwitch( - ContentSwitches.ENABLE_ACCESSIBILITY_SCRIPT_INJECTION)) { - return false; - } - - if (!mContentViewClient.isJavascriptEnabled()) { - return false; - } - - int result = getContext().checkCallingOrSelfPermission( - android.Manifest.permission.INTERNET); - if (result != PackageManager.PERMISSION_GRANTED) { - return false; - } - - Field field = Settings.Secure.class.getField("ACCESSIBILITY_SCRIPT_INJECTION"); - field.setAccessible(true); - String accessibilityScriptInjection = (String) field.get(null); - ContentResolver contentResolver = getContext().getContentResolver(); - - if (mAccessibilityScriptInjectionObserver == null) { - ContentObserver contentObserver = new ContentObserver(new Handler()) { - @Override - public void onChange(boolean selfChange, Uri uri) { - setAccessibilityState(mAccessibilityManager.isEnabled()); - } - }; - contentResolver.registerContentObserver( - Settings.Secure.getUriFor(accessibilityScriptInjection), - false, - contentObserver); - mAccessibilityScriptInjectionObserver = contentObserver; - } - - return Settings.Secure.getInt(contentResolver, accessibilityScriptInjection, 0) == 1; - } catch (NoSuchFieldException e) { - // Do nothing, default to false. - } catch (IllegalAccessException e) { - // Do nothing, default to false. - } - return false; - } - - /** - * Returns whether or not accessibility injection is being used. - */ - public boolean isInjectingAccessibilityScript() { - return mAccessibilityInjector.accessibilityIsAvailable(); - } - - /** * Returns true if accessibility is on and touch exploration is enabled. */ public boolean isTouchExplorationEnabled() { @@ -3059,33 +2940,15 @@ */ public void setAccessibilityState(boolean state) { if (!state) { - setInjectedAccessibility(false); mNativeAccessibilityAllowed = false; mTouchExplorationEnabled = false; } else { - boolean useScriptInjection = isDeviceAccessibilityScriptInjectionEnabled(); - setInjectedAccessibility(useScriptInjection); - mNativeAccessibilityAllowed = !useScriptInjection; + mNativeAccessibilityAllowed = true; mTouchExplorationEnabled = mAccessibilityManager.isTouchExplorationEnabled(); } } /** - * Enable or disable injected accessibility features - */ - public void setInjectedAccessibility(boolean enabled) { - mAccessibilityInjector.addOrRemoveAccessibilityApisIfNecessary(); - mAccessibilityInjector.setScriptEnabled(enabled); - } - - /** - * Stop any TTS notifications that are currently going on. - */ - public void stopCurrentAccessibilityNotifications() { - mAccessibilityInjector.onPageLostFocus(); - } - - /** * Return whether or not we should set accessibility focus on page load. */ public boolean shouldSetAccessibilityFocusOnPageLoad() {
diff --git a/content/public/android/java/src/org/chromium/content/browser/JellyBeanContentView.java b/content/public/android/java/src/org/chromium/content/browser/JellyBeanContentView.java deleted file mode 100644 index 88dd3bc..0000000 --- a/content/public/android/java/src/org/chromium/content/browser/JellyBeanContentView.java +++ /dev/null
@@ -1,37 +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.content.browser; - -import android.content.Context; -import android.os.Bundle; -import android.view.accessibility.AccessibilityNodeProvider; - -/** - * A version of {@link ContentView} that supports JellyBean features. - */ -class JellyBeanContentView extends ContentView { - JellyBeanContentView(Context context, ContentViewCore cvc) { - super(context, cvc); - } - - @Override - public boolean performAccessibilityAction(int action, Bundle arguments) { - if (mContentViewCore.supportsAccessibilityAction(action)) { - return mContentViewCore.performAccessibilityAction(action, arguments); - } - - return super.performAccessibilityAction(action, arguments); - } - - @Override - public AccessibilityNodeProvider getAccessibilityNodeProvider() { - AccessibilityNodeProvider provider = mContentViewCore.getAccessibilityNodeProvider(); - if (provider != null) { - return provider; - } else { - return super.getAccessibilityNodeProvider(); - } - } -}
diff --git a/content/public/android/java/src/org/chromium/content/browser/ServiceRegistrar.java b/content/public/android/java/src/org/chromium/content/browser/ServiceRegistrar.java index e18b3c60..6065fa5 100644 --- a/content/public/android/java/src/org/chromium/content/browser/ServiceRegistrar.java +++ b/content/public/android/java/src/org/chromium/content/browser/ServiceRegistrar.java
@@ -39,4 +39,10 @@ registry.addService(BatteryMonitor.MANAGER, new BatteryMonitorImplementationFactory(applicationContext)); } + + @CalledByNative + static void registerFrameHostServices(ServiceRegistry registry, Context applicationContext) { + assert applicationContext != null; + // TODO(avayvod): Register the PresentationService implementation here. + } }
diff --git a/content/public/android/java/src/org/chromium/content/browser/SpeechRecognition.java b/content/public/android/java/src/org/chromium/content/browser/SpeechRecognition.java index 974191e..6d535cb 100644 --- a/content/public/android/java/src/org/chromium/content/browser/SpeechRecognition.java +++ b/content/public/android/java/src/org/chromium/content/browser/SpeechRecognition.java
@@ -95,7 +95,7 @@ // Translate Android SpeechRecognizer errors to Web Speech API errors. switch(error) { case SpeechRecognizer.ERROR_AUDIO: - code = SpeechRecognitionErrorCode.AUDIO; + code = SpeechRecognitionErrorCode.AUDIO_CAPTURE; break; case SpeechRecognizer.ERROR_CLIENT: code = SpeechRecognitionErrorCode.ABORTED;
diff --git a/content/public/android/java/src/org/chromium/content/browser/accessibility/AccessibilityInjector.java b/content/public/android/java/src/org/chromium/content/browser/accessibility/AccessibilityInjector.java deleted file mode 100644 index d07c4931..0000000 --- a/content/public/android/java/src/org/chromium/content/browser/accessibility/AccessibilityInjector.java +++ /dev/null
@@ -1,457 +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.content.browser.accessibility; - -import android.accessibilityservice.AccessibilityServiceInfo; -import android.content.Context; -import android.content.pm.PackageManager; -import android.os.Build; -import android.os.Bundle; -import android.os.Vibrator; -import android.speech.tts.TextToSpeech; -import android.view.View; -import android.view.accessibility.AccessibilityManager; -import android.view.accessibility.AccessibilityNodeInfo; - -import org.apache.http.NameValuePair; -import org.apache.http.client.utils.URLEncodedUtils; -import org.chromium.base.CommandLine; -import org.chromium.content.browser.ContentViewCore; -import org.chromium.content.browser.JavascriptInterface; -import org.chromium.content.common.ContentSwitches; -import org.json.JSONException; -import org.json.JSONObject; - -import java.net.URI; -import java.net.URISyntaxException; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; - -/** - * Responsible for accessibility injection and management of a {@link ContentViewCore}. - */ -public class AccessibilityInjector { - // The ContentView this injector is responsible for managing. - protected ContentViewCore mContentViewCore; - - // The Java objects that are exposed to JavaScript - private TextToSpeechWrapper mTextToSpeech; - private VibratorWrapper mVibrator; - private final boolean mHasVibratePermission; - - // Lazily loaded helper objects. - private AccessibilityManager mAccessibilityManager; - - // Whether or not we should be injecting the script. - protected boolean mInjectedScriptEnabled; - protected boolean mScriptInjected; - - private final String mAccessibilityScreenReaderUrl; - - // To support building against the JELLY_BEAN and not JELLY_BEAN_MR1 SDK we need to add this - // constant here. - private static final int FEEDBACK_BRAILLE = 0x00000020; - - // constants for determining script injection strategy - private static final int ACCESSIBILITY_SCRIPT_INJECTION_UNDEFINED = -1; - private static final int ACCESSIBILITY_SCRIPT_INJECTION_OPTED_OUT = 0; - private static final int ACCESSIBILITY_SCRIPT_INJECTION_PROVIDED = 1; - private static final String ALIAS_ACCESSIBILITY_JS_INTERFACE = "accessibility"; - private static final String ALIAS_ACCESSIBILITY_JS_INTERFACE_2 = "accessibility2"; - - // Template for JavaScript that injects a screen-reader. - private static final String DEFAULT_ACCESSIBILITY_SCREEN_READER_URL = - "https://ssl.gstatic.com/accessibility/javascript/android/chromeandroidvox.js"; - - private static final String ACCESSIBILITY_SCREEN_READER_JAVASCRIPT_TEMPLATE = - "(function() {" - + " var chooser = document.createElement('script');" - + " chooser.type = 'text/javascript';" - + " chooser.src = '%1s';" - + " document.getElementsByTagName('head')[0].appendChild(chooser);" - + " })();"; - - // JavaScript call to turn ChromeVox on or off. - private static final String TOGGLE_CHROME_VOX_JAVASCRIPT = - "(function() {" - + " if (typeof cvox !== 'undefined') {" - + " cvox.ChromeVox.host.activateOrDeactivateChromeVox(%1s);" - + " }" - + " })();"; - - /** - * Returns an instance of the {@link AccessibilityInjector} based on the SDK version. - * @param view The ContentViewCore that this AccessibilityInjector manages. - * @return An instance of a {@link AccessibilityInjector}. - */ - public static AccessibilityInjector newInstance(ContentViewCore view) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - return new LollipopAccessibilityInjector(view); - } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { - return new JellyBeanAccessibilityInjector(view); - } else { - return new AccessibilityInjector(view); - } - } - - /** - * Creates an instance of the IceCreamSandwichAccessibilityInjector. - * @param view The ContentViewCore that this AccessibilityInjector manages. - */ - protected AccessibilityInjector(ContentViewCore view) { - mContentViewCore = view; - - mAccessibilityScreenReaderUrl = CommandLine.getInstance().getSwitchValue( - ContentSwitches.ACCESSIBILITY_JAVASCRIPT_URL, - DEFAULT_ACCESSIBILITY_SCREEN_READER_URL); - - mHasVibratePermission = mContentViewCore.getContext().checkCallingOrSelfPermission( - android.Manifest.permission.VIBRATE) == PackageManager.PERMISSION_GRANTED; - } - - /** - * Injects a <script> tag into the current web site that pulls in the ChromeVox script for - * accessibility support. Only injects if accessibility is turned on by - * {@link AccessibilityManager#isEnabled()}, accessibility script injection is turned on, and - * javascript is enabled on this page. - * - * @see AccessibilityManager#isEnabled() - */ - public void injectAccessibilityScriptIntoPage() { - if (!accessibilityIsAvailable()) return; - - int axsParameterValue = getAxsUrlParameterValue(); - if (axsParameterValue != ACCESSIBILITY_SCRIPT_INJECTION_UNDEFINED) { - return; - } - - String js = getScreenReaderInjectingJs(); - if (mContentViewCore.isDeviceAccessibilityScriptInjectionEnabled() - && js != null && mContentViewCore.isAlive()) { - addOrRemoveAccessibilityApisIfNecessary(); - mContentViewCore.getWebContents().evaluateJavaScript(js, null); - mInjectedScriptEnabled = true; - mScriptInjected = true; - } - } - - /** - * Handles adding or removing accessibility related Java objects ({@link TextToSpeech} and - * {@link Vibrator}) interfaces from Javascript. This method should be called at a time when it - * is safe to add or remove these interfaces, specifically when the {@link ContentViewCore} is - * first initialized or right before the {@link ContentViewCore} is about to navigate to a URL - * or reload. - * <p> - * If this method is called at other times, the interfaces might not be correctly removed, - * meaning that Javascript can still access these Java objects that may have been already - * shut down. - */ - public void addOrRemoveAccessibilityApisIfNecessary() { - if (accessibilityIsAvailable()) { - addAccessibilityApis(); - } else { - removeAccessibilityApis(); - } - } - - /** - * Checks whether or not touch to explore is enabled on the system. - */ - public boolean accessibilityIsAvailable() { - if (!getAccessibilityManager().isEnabled() - || !mContentViewCore.getContentViewClient().isJavascriptEnabled()) { - return false; - } - - try { - // Check that there is actually a service running that requires injecting this script. - List<AccessibilityServiceInfo> services = - getAccessibilityManager().getEnabledAccessibilityServiceList( - FEEDBACK_BRAILLE | AccessibilityServiceInfo.FEEDBACK_SPOKEN); - return services.size() > 0; - } catch (NullPointerException e) { - // getEnabledAccessibilityServiceList() can throw an NPE due to a bad - // AccessibilityService. - return false; - } - } - - /** - * Sets whether or not the script is enabled. If the script is disabled, we also stop any - * we output that is occurring. If the script has not yet been injected, injects it. - * @param enabled Whether or not to enable the script. - */ - public void setScriptEnabled(boolean enabled) { - if (enabled && !mScriptInjected) injectAccessibilityScriptIntoPage(); - if (!accessibilityIsAvailable() || mInjectedScriptEnabled == enabled) return; - - mInjectedScriptEnabled = enabled; - if (mContentViewCore.isAlive()) { - String js = String.format(TOGGLE_CHROME_VOX_JAVASCRIPT, Boolean.toString( - mInjectedScriptEnabled)); - mContentViewCore.getWebContents().evaluateJavaScript(js, null); - - if (!mInjectedScriptEnabled) { - // Stop any TTS/Vibration right now. - onPageLostFocus(); - } - } - } - - /** - * Notifies this handler that a page load has started, which means we should mark the - * accessibility script as not being injected. This way we can properly ignore incoming - * accessibility gesture events. - */ - public void onPageLoadStarted() { - mScriptInjected = false; - } - - /** - * Notifies this handler that a page load has stopped, which means we can now inject the - * accessibility script. - */ - public void onPageLoadStopped() { - injectAccessibilityScriptIntoPage(); - } - - /** - * Stop any notifications that are currently going on (e.g. Text-to-Speech). - */ - public void onPageLostFocus() { - if (mContentViewCore.isAlive()) { - if (mTextToSpeech != null) mTextToSpeech.stop(); - if (mVibrator != null) mVibrator.cancel(); - } - } - - /** - * Initializes an {@link AccessibilityNodeInfo} with the actions and movement granularity - * levels supported by this {@link AccessibilityInjector}. - * <p> - * If an action identifier is added in this method, this {@link AccessibilityInjector} should - * also return {@code true} from {@link #supportsAccessibilityAction(int)}. - * </p> - * - * @param info The info to initialize. - * @see View#onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo) - */ - public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { } - - /** - * Returns {@code true} if this {@link AccessibilityInjector} should handle the specified - * action. - * - * @param action An accessibility action identifier. - * @return {@code true} if this {@link AccessibilityInjector} should handle the specified - * action. - */ - public boolean supportsAccessibilityAction(int action) { - return false; - } - - /** - * Performs the specified accessibility action. - * - * @param action The identifier of the action to perform. - * @param arguments The action arguments, or {@code null} if no arguments. - * @return {@code true} if the action was successful. - * @see View#performAccessibilityAction(int, Bundle) - */ - public boolean performAccessibilityAction(int action, Bundle arguments) { - return false; - } - - protected void addAccessibilityApis() { - Context context = mContentViewCore.getContext(); - if (context != null) { - // Enabled, we should try to add if we have to. - if (mTextToSpeech == null) { - mTextToSpeech = createTextToSpeechWrapper(mContentViewCore.getContainerView(), - context); - mContentViewCore.addJavascriptInterface(mTextToSpeech, - ALIAS_ACCESSIBILITY_JS_INTERFACE); - } - - if (mVibrator == null && mHasVibratePermission) { - mVibrator = new VibratorWrapper(context); - mContentViewCore.addJavascriptInterface(mVibrator, - ALIAS_ACCESSIBILITY_JS_INTERFACE_2); - } - } - } - - protected void removeAccessibilityApis() { - if (mTextToSpeech != null) { - mContentViewCore.removeJavascriptInterface(ALIAS_ACCESSIBILITY_JS_INTERFACE); - mTextToSpeech.stop(); - mTextToSpeech.shutdownInternal(); - mTextToSpeech = null; - } - - if (mVibrator != null) { - mContentViewCore.removeJavascriptInterface(ALIAS_ACCESSIBILITY_JS_INTERFACE_2); - mVibrator.cancel(); - mVibrator = null; - } - } - - private int getAxsUrlParameterValue() { - if (mContentViewCore.getWebContents().getUrl() == null) { - return ACCESSIBILITY_SCRIPT_INJECTION_UNDEFINED; - } - - try { - List<NameValuePair> params = URLEncodedUtils.parse( - new URI(mContentViewCore.getWebContents().getUrl()), null); - - for (NameValuePair param : params) { - if ("axs".equals(param.getName())) { - return Integer.parseInt(param.getValue()); - } - } - } catch (URISyntaxException ex) { - // Intentional no-op. - } catch (NumberFormatException ex) { - // Intentional no-op. - } catch (IllegalArgumentException ex) { - // Intentional no-op. - } - - return ACCESSIBILITY_SCRIPT_INJECTION_UNDEFINED; - } - - private String getScreenReaderInjectingJs() { - return String.format(ACCESSIBILITY_SCREEN_READER_JAVASCRIPT_TEMPLATE, - mAccessibilityScreenReaderUrl); - } - - private AccessibilityManager getAccessibilityManager() { - if (mAccessibilityManager == null) { - mAccessibilityManager = (AccessibilityManager) mContentViewCore.getContext() - .getSystemService(Context.ACCESSIBILITY_SERVICE); - } - - return mAccessibilityManager; - } - - /** - * Used to protect how long JavaScript can vibrate for. This isn't a good comprehensive - * protection, just used to cover mistakes and protect against long vibrate durations/repeats. - * - * Also only exposes methods we *want* to expose, no others for the class. - */ - private static class VibratorWrapper { - private static final long MAX_VIBRATE_DURATION_MS = 5000; - - private final Vibrator mVibrator; - - public VibratorWrapper(Context context) { - mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE); - } - - @JavascriptInterface - @SuppressWarnings("unused") - public boolean hasVibrator() { - return mVibrator.hasVibrator(); - } - - @JavascriptInterface - @SuppressWarnings("unused") - public void vibrate(long milliseconds) { - milliseconds = Math.min(milliseconds, MAX_VIBRATE_DURATION_MS); - mVibrator.vibrate(milliseconds); - } - - @JavascriptInterface - @SuppressWarnings("unused") - public void vibrate(long[] pattern, int repeat) { - for (int i = 0; i < pattern.length; ++i) { - pattern[i] = Math.min(pattern[i], MAX_VIBRATE_DURATION_MS); - } - - repeat = -1; - - mVibrator.vibrate(pattern, repeat); - } - - @JavascriptInterface - @SuppressWarnings("unused") - public void cancel() { - mVibrator.cancel(); - } - } - - protected TextToSpeechWrapper createTextToSpeechWrapper(View view, Context context) { - return new TextToSpeechWrapper(view, context); - } - - /** - * Used to protect the TextToSpeech class, only exposing the methods we want to expose. - */ - protected static class TextToSpeechWrapper { - protected final TextToSpeech mTextToSpeech; - private final View mView; - - protected TextToSpeechWrapper(View view, Context context) { - mView = view; - mTextToSpeech = new TextToSpeech(context, null, null); - } - - @JavascriptInterface - @SuppressWarnings("unused") - public boolean isSpeaking() { - return mTextToSpeech.isSpeaking(); - } - - @JavascriptInterface - @SuppressWarnings({"unused", "deprecation"}) - public int speak(String text, int queueMode, String jsonParams) { - // Try to pull the params from the JSON string. - HashMap<String, String> params = null; - try { - if (jsonParams != null) { - params = new HashMap<String, String>(); - JSONObject json = new JSONObject(jsonParams); - - // Using legacy API here. - @SuppressWarnings("unchecked") - Iterator<String> keyIt = json.keys(); - - while (keyIt.hasNext()) { - String key = keyIt.next(); - // Only add parameters that are raw data types. - if (json.optJSONObject(key) == null && json.optJSONArray(key) == null) { - params.put(key, json.getString(key)); - } - } - } - } catch (JSONException e) { - params = null; - } - - return mTextToSpeech.speak(text, queueMode, params); - } - - @JavascriptInterface - @SuppressWarnings("unused") - public int stop() { - return mTextToSpeech.stop(); - } - - @JavascriptInterface - @SuppressWarnings("unused") - public void braille(String jsonString) { - // This is here because AndroidVox depends on the existence - // of this method. - } - - @SuppressWarnings("unused") - protected void shutdownInternal() { - mTextToSpeech.shutdown(); - } - } -}
diff --git a/content/public/android/java/src/org/chromium/content/browser/accessibility/JellyBeanAccessibilityInjector.java b/content/public/android/java/src/org/chromium/content/browser/accessibility/JellyBeanAccessibilityInjector.java deleted file mode 100644 index 67b4dca..0000000 --- a/content/public/android/java/src/org/chromium/content/browser/accessibility/JellyBeanAccessibilityInjector.java +++ /dev/null
@@ -1,268 +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.content.browser.accessibility; - -import android.annotation.TargetApi; -import android.content.Context; -import android.os.Build; -import android.os.Bundle; -import android.os.SystemClock; -import android.view.accessibility.AccessibilityNodeInfo; - -import org.chromium.content.browser.ContentViewCore; -import org.chromium.content.browser.JavascriptInterface; -import org.json.JSONException; -import org.json.JSONObject; - -import java.util.Iterator; -import java.util.Locale; -import java.util.concurrent.atomic.AtomicInteger; - -/** - * Handles injecting accessibility Javascript and related Javascript -> Java APIs for JB and newer - * devices. - */ -@TargetApi(Build.VERSION_CODES.JELLY_BEAN) -class JellyBeanAccessibilityInjector extends AccessibilityInjector { - private CallbackHandler mCallback; - private JSONObject mAccessibilityJSONObject; - - private static final String ALIAS_TRAVERSAL_JS_INTERFACE = "accessibilityTraversal"; - - // Template for JavaScript that performs AndroidVox actions. - private static final String ACCESSIBILITY_ANDROIDVOX_TEMPLATE = - "cvox.AndroidVox.performAction('%1s')"; - - /** - * Constructs an instance of the JellyBeanAccessibilityInjector. - * @param view The ContentViewCore that this AccessibilityInjector manages. - */ - protected JellyBeanAccessibilityInjector(ContentViewCore view) { - super(view); - } - - @Override - @SuppressWarnings("deprecation") - public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { - info.setMovementGranularities(AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER - | AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD - | AccessibilityNodeInfo.MOVEMENT_GRANULARITY_LINE - | AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH - | AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PAGE); - info.addAction(AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY); - info.addAction(AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY); - info.addAction(AccessibilityNodeInfo.ACTION_NEXT_HTML_ELEMENT); - info.addAction(AccessibilityNodeInfo.ACTION_PREVIOUS_HTML_ELEMENT); - info.addAction(AccessibilityNodeInfo.ACTION_CLICK); - info.setClickable(true); - } - - @Override - public boolean supportsAccessibilityAction(int action) { - if (action == AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY - || action == AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY - || action == AccessibilityNodeInfo.ACTION_NEXT_HTML_ELEMENT - || action == AccessibilityNodeInfo.ACTION_PREVIOUS_HTML_ELEMENT - || action == AccessibilityNodeInfo.ACTION_CLICK) { - return true; - } - - return false; - } - - @Override - public boolean performAccessibilityAction(int action, Bundle arguments) { - if (!accessibilityIsAvailable() || !mContentViewCore.isAlive() - || !mInjectedScriptEnabled || !mScriptInjected) { - return false; - } - - boolean actionSuccessful = sendActionToAndroidVox(action, arguments); - - if (actionSuccessful) mContentViewCore.getWebContents().showImeIfNeeded(); - - return actionSuccessful; - } - - @Override - protected void addAccessibilityApis() { - super.addAccessibilityApis(); - - Context context = mContentViewCore.getContext(); - if (context != null && mCallback == null) { - mCallback = new CallbackHandler(ALIAS_TRAVERSAL_JS_INTERFACE); - mContentViewCore.addJavascriptInterface(mCallback, ALIAS_TRAVERSAL_JS_INTERFACE); - } - } - - @Override - protected void removeAccessibilityApis() { - super.removeAccessibilityApis(); - - if (mCallback != null) { - mContentViewCore.removeJavascriptInterface(ALIAS_TRAVERSAL_JS_INTERFACE); - mCallback = null; - } - } - - /** - * Packs an accessibility action into a JSON object and sends it to AndroidVox. - * - * @param action The action identifier. - * @param arguments The action arguments, if applicable. - * @return The result of the action. - */ - private boolean sendActionToAndroidVox(int action, Bundle arguments) { - if (mCallback == null) return false; - if (mAccessibilityJSONObject == null) { - mAccessibilityJSONObject = new JSONObject(); - } else { - // Remove all keys from the object. - final Iterator<?> keys = mAccessibilityJSONObject.keys(); - while (keys.hasNext()) { - keys.next(); - keys.remove(); - } - } - - try { - mAccessibilityJSONObject.accumulate("action", action); - if (arguments != null) { - if (action == AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY || action - == AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY) { - final int granularity = arguments.getInt( - AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT); - mAccessibilityJSONObject.accumulate("granularity", granularity); - } else if (action == AccessibilityNodeInfo.ACTION_NEXT_HTML_ELEMENT - || action == AccessibilityNodeInfo.ACTION_PREVIOUS_HTML_ELEMENT) { - final String element = arguments.getString( - AccessibilityNodeInfo.ACTION_ARGUMENT_HTML_ELEMENT_STRING); - mAccessibilityJSONObject.accumulate("element", element); - } - } - } catch (JSONException ex) { - return false; - } - - final String jsonString = mAccessibilityJSONObject.toString(); - final String jsCode = String.format(Locale.US, ACCESSIBILITY_ANDROIDVOX_TEMPLATE, - jsonString); - return mCallback.performAction(mContentViewCore, jsCode); - } - - private static class CallbackHandler { - private static final String JAVASCRIPT_ACTION_TEMPLATE = "(function() {" - + " retVal = false;" - + " try {" - + " retVal = %s;" - + " } catch (e) {" - + " retVal = false;" - + " }" - + " %s.onResult(%d, retVal);" - + "})()"; - - // Time in milliseconds to wait for a result before failing. - private static final long RESULT_TIMEOUT = 5000; - - private final AtomicInteger mResultIdCounter = new AtomicInteger(); - private final Object mResultLock = new Object(); - private final String mInterfaceName; - - private boolean mResult = false; - private long mResultId = -1; - - private CallbackHandler(String interfaceName) { - mInterfaceName = interfaceName; - } - - /** - * Performs an action and attempts to wait for a result. - * - * @param contentView The ContentViewCore to perform the action on. - * @param code Javascript code that evaluates to a result. - * @return The result of the action. - */ - private boolean performAction(ContentViewCore contentView, String code) { - final int resultId = mResultIdCounter.getAndIncrement(); - final String js = String.format(Locale.US, JAVASCRIPT_ACTION_TEMPLATE, code, - mInterfaceName, resultId); - contentView.getWebContents().evaluateJavaScript(js, null); - - return getResultAndClear(resultId); - } - - /** - * Gets the result of a request to perform an accessibility action. - * - * @param resultId The result id to match the result with the request. - * @return The result of the request. - */ - private boolean getResultAndClear(int resultId) { - synchronized (mResultLock) { - final boolean success = waitForResultTimedLocked(resultId); - final boolean result = success ? mResult : false; - clearResultLocked(); - return result; - } - } - - /** - * Clears the result state. - */ - private void clearResultLocked() { - mResultId = -1; - mResult = false; - } - - /** - * Waits up to a given bound for a result of a request and returns it. - * - * @param resultId The result id to match the result with the request. - * @return Whether the result was received. - */ - private boolean waitForResultTimedLocked(int resultId) { - long waitTimeMillis = RESULT_TIMEOUT; - final long startTimeMillis = SystemClock.uptimeMillis(); - while (true) { - try { - if (mResultId == resultId) return true; - if (mResultId > resultId) return false; - final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis; - waitTimeMillis = RESULT_TIMEOUT - elapsedTimeMillis; - if (waitTimeMillis <= 0) return false; - mResultLock.wait(waitTimeMillis); - } catch (InterruptedException ie) { - /* ignore */ - } - } - } - - /** - * Callback exposed to JavaScript. Handles returning the result of a - * request to a waiting (or potentially timed out) thread. - * - * @param id The result id of the request as a {@link String}. - * @param result The result of a request as a {@link String}. - */ - @JavascriptInterface - @SuppressWarnings("unused") - public void onResult(String id, String result) { - final long resultId; - try { - resultId = Long.parseLong(id); - } catch (NumberFormatException e) { - return; - } - - synchronized (mResultLock) { - if (resultId > mResultId) { - mResult = Boolean.parseBoolean(result); - mResultId = resultId; - } - mResultLock.notifyAll(); - } - } - } -}
diff --git a/content/public/android/java/src/org/chromium/content/browser/accessibility/LollipopAccessibilityInjector.java b/content/public/android/java/src/org/chromium/content/browser/accessibility/LollipopAccessibilityInjector.java deleted file mode 100644 index d7737eb5..0000000 --- a/content/public/android/java/src/org/chromium/content/browser/accessibility/LollipopAccessibilityInjector.java +++ /dev/null
@@ -1,93 +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.content.browser.accessibility; - -import android.annotation.TargetApi; -import android.content.Context; -import android.os.Build; -import android.os.Bundle; -import android.view.View; -import android.view.accessibility.AccessibilityNodeInfo; - -import org.chromium.content.browser.ContentViewCore; -import org.chromium.content.browser.JavascriptInterface; -import org.json.JSONException; -import org.json.JSONObject; - -import java.util.Iterator; - -/** - * Handles injecting accessibility Javascript and related Javascript -> Java APIs for Lollipop and - * newer devices. - */ -@TargetApi(Build.VERSION_CODES.LOLLIPOP) -class LollipopAccessibilityInjector extends JellyBeanAccessibilityInjector { - /** - * Constructs an instance of the LollipopAccessibilityInjector. - * @param view The ContentViewCore that this AccessibilityInjector manages. - */ - protected LollipopAccessibilityInjector(ContentViewCore view) { - super(view); - } - - @Override - public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { - info.setMovementGranularities( - AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER - | AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD - | AccessibilityNodeInfo.MOVEMENT_GRANULARITY_LINE - | AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH - | AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PAGE); - info.addAction( - AccessibilityNodeInfo.AccessibilityAction.ACTION_NEXT_AT_MOVEMENT_GRANULARITY); - info.addAction( - AccessibilityNodeInfo.AccessibilityAction.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY); - info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_NEXT_HTML_ELEMENT); - info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_PREVIOUS_HTML_ELEMENT); - info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK); - info.setClickable(true); - } - - @Override - protected TextToSpeechWrapper createTextToSpeechWrapper(View view, Context context) { - return new LTextToSpeechWrapper(view, context); - } - - protected static class LTextToSpeechWrapper extends AccessibilityInjector.TextToSpeechWrapper { - private LTextToSpeechWrapper(View view, Context context) { - super(view, context); - } - - @Override - @JavascriptInterface - @SuppressWarnings("unused") - public int speak(String text, int queueMode, String jsonParams) { - // Try to pull the params from the JSON string. - Bundle bundle = null; - try { - if (jsonParams != null) { - bundle = new Bundle(); - JSONObject json = new JSONObject(jsonParams); - - // Using legacy API here. - @SuppressWarnings("unchecked") - Iterator<String> keyIt = json.keys(); - - while (keyIt.hasNext()) { - String key = keyIt.next(); - // Only add parameters that are raw data types. - if (json.optJSONObject(key) == null && json.optJSONArray(key) == null) { - bundle.putCharSequence(key, json.getString(key)); - } - } - } - } catch (JSONException e) { - bundle = null; - } - - return mTextToSpeech.speak(text, queueMode, bundle, null); - } - } -}
diff --git a/content/public/android/java/src/org/chromium/content/browser/accessibility/captioning/EmptyCaptioningBridge.java b/content/public/android/java/src/org/chromium/content/browser/accessibility/captioning/EmptyCaptioningBridge.java index da25683..8ae257f9 100644 --- a/content/public/android/java/src/org/chromium/content/browser/accessibility/captioning/EmptyCaptioningBridge.java +++ b/content/public/android/java/src/org/chromium/content/browser/accessibility/captioning/EmptyCaptioningBridge.java
@@ -12,10 +12,18 @@ /** * A no-op implementation of the syncToDelegate function. */ + @Override public void syncToDelegate() {} /** - * A no-op implementation of the destroy function. + * No-op implementation of registerBridge. */ - public void destroy() {} + @Override + public void registerBridge() {} + + /** + * A no-op implementation of the unregisterBridge function. + */ + @Override + public void unregisterBridge() {} }
diff --git a/content/public/android/java/src/org/chromium/content/browser/accessibility/captioning/KitKatCaptioningBridge.java b/content/public/android/java/src/org/chromium/content/browser/accessibility/captioning/KitKatCaptioningBridge.java index 73feeea..04b16e8 100644 --- a/content/public/android/java/src/org/chromium/content/browser/accessibility/captioning/KitKatCaptioningBridge.java +++ b/content/public/android/java/src/org/chromium/content/browser/accessibility/captioning/KitKatCaptioningBridge.java
@@ -63,13 +63,12 @@ mCaptioningManager = (CaptioningManager) contenViewCore.getContext() .getApplicationContext() .getSystemService(Context.CAPTIONING_SERVICE); - mCaptioningManager.addCaptioningChangeListener(mCaptioningChangeListener); - syncToDelegate(); } /** * Force-sync the current closed caption settings to the delegate */ + @Override public void syncToDelegate() { mCaptioningChangeDelegate.onEnabledChanged(mCaptioningManager.isEnabled()); mCaptioningChangeDelegate.onFontScaleChanged(mCaptioningManager.getFontScale()); @@ -79,10 +78,19 @@ } /** - * De-register this bridge from the system captioning manager. This bridge - * should not be used again after this is called. + * Register this bridge for event changes with the system CaptioningManager. */ - public void destroy() { + @Override + public void registerBridge() { + mCaptioningManager.addCaptioningChangeListener(mCaptioningChangeListener); + syncToDelegate(); + } + + /** + * De-register this bridge from the system captioning manager. + */ + @Override + public void unregisterBridge() { mCaptioningManager.removeCaptioningChangeListener(mCaptioningChangeListener); }
diff --git a/content/public/android/java/src/org/chromium/content/browser/accessibility/captioning/SystemCaptioningBridge.java b/content/public/android/java/src/org/chromium/content/browser/accessibility/captioning/SystemCaptioningBridge.java index 55eb5afb..c640622d 100644 --- a/content/public/android/java/src/org/chromium/content/browser/accessibility/captioning/SystemCaptioningBridge.java +++ b/content/public/android/java/src/org/chromium/content/browser/accessibility/captioning/SystemCaptioningBridge.java
@@ -14,8 +14,12 @@ public void syncToDelegate(); /** - * Removes any external listeners that were added. This implementation doesn't do anything. - * After destroy is called, this object should not be used again. + * Register this bridge for event changes with the system CaptioningManager. */ - public void destroy(); + public void registerBridge(); + + /** + * Unregister this bridge from system CaptionManager. Must be called to avoid leaks. + */ + public void unregisterBridge(); }
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/ContentCommandLineTest.java b/content/public/android/javatests/src/org/chromium/content/browser/ContentCommandLineTest.java index 6b74559a..145ed5a9 100644 --- a/content/public/android/javatests/src/org/chromium/content/browser/ContentCommandLineTest.java +++ b/content/public/android/javatests/src/org/chromium/content/browser/ContentCommandLineTest.java
@@ -17,14 +17,14 @@ public class ContentCommandLineTest extends NativeLibraryTestBase { // A reference command line. Note that switch2 is [brea\d], switch3 is [and "butter"], // and switch4 is [a "quoted" 'food'!] - static final String INIT_SWITCHES[] = { "init_command", "--SWITCH", "Arg", + static final String INIT_SWITCHES[] = { "init_command", "--switch", "Arg", "--switch2=brea\\d", "--switch3=and \"butter\"", "--switch4=a \"quoted\" 'food'!", "--", "--actually_an_arg" }; // The same command line, but in quoted string format. static final char INIT_SWITCHES_BUFFER[] = - ("init_command --SWITCH Arg --switch2=brea\\d --switch3=\"and \\\"butt\"er\\\" " + ("init_command --switch Arg --switch2=brea\\d --switch3=\"and \\\"butt\"er\\\" " + "--switch4='a \"quoted\" \\'food\\'!' " + "-- --actually_an_arg").toCharArray(); @@ -46,15 +46,14 @@ void checkInitSwitches() { CommandLine cl = CommandLine.getInstance(); assertFalse(cl.hasSwitch("init_command")); - assertFalse(cl.hasSwitch("switch")); - assertTrue(cl.hasSwitch("SWITCH")); - assertFalse(cl.hasSwitch("--SWITCH")); - assertFalse(cl.hasSwitch("Arg")); + assertTrue(cl.hasSwitch("switch")); + assertFalse(cl.hasSwitch("--switch")); + assertFalse(cl.hasSwitch("arg")); assertFalse(cl.hasSwitch("actually_an_arg")); assertEquals("brea\\d", cl.getSwitchValue("switch2")); assertEquals("and \"butter\"", cl.getSwitchValue("switch3")); assertEquals("a \"quoted\" 'food'!", cl.getSwitchValue("switch4")); - assertNull(cl.getSwitchValue("SWITCH")); + assertNull(cl.getSwitchValue("switch")); assertNull(cl.getSwitchValue("non-existant")); }
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/input/GamepadMappingsTest.java b/content/public/android/javatests/src/org/chromium/content/browser/input/GamepadMappingsTest.java deleted file mode 100644 index 5ae486d5..0000000 --- a/content/public/android/javatests/src/org/chromium/content/browser/input/GamepadMappingsTest.java +++ /dev/null
@@ -1,294 +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.content.browser.input; - -import android.test.InstrumentationTestCase; -import android.test.suitebuilder.annotation.SmallTest; -import android.view.KeyEvent; -import android.view.MotionEvent; - -import org.chromium.base.test.util.Feature; - -import java.util.Arrays; -import java.util.BitSet; - -/** - * Verify no regressions in gamepad mappings. - */ -public class GamepadMappingsTest extends InstrumentationTestCase { - /** - * Set bits indicate that we don't expect the button at mMappedButtons[index] to be mapped. - */ - private BitSet mUnmappedButtons = new BitSet(CanonicalButtonIndex.COUNT); - /** - * Set bits indicate that we don't expect the axis at mMappedAxes[index] to be mapped. - */ - private BitSet mUnmappedAxes = new BitSet(CanonicalAxisIndex.COUNT); - private float[] mMappedButtons = new float[CanonicalButtonIndex.COUNT]; - private float[] mMappedAxes = new float[CanonicalAxisIndex.COUNT]; - private float[] mRawButtons = new float[GamepadDevice.MAX_RAW_BUTTON_VALUES]; - private float[] mRawAxes = new float[GamepadDevice.MAX_RAW_AXIS_VALUES]; - - @Override - protected void setUp() throws Exception { - super.setUp(); - - // By default, we expect every button and axis to be mapped. - mUnmappedButtons.clear(); - mUnmappedAxes.clear(); - - // Start with all the mapped values as unmapped. - Arrays.fill(mMappedButtons, Float.NaN); - Arrays.fill(mMappedAxes, Float.NaN); - - // Set each raw value to something unique. - for (int i = 0; i < GamepadDevice.MAX_RAW_AXIS_VALUES; i++) { - mRawAxes[i] = -i - 1.0f; - } - for (int i = 0; i < GamepadDevice.MAX_RAW_BUTTON_VALUES; i++) { - mRawButtons[i] = i + 1.0f; - } - } - - @SmallTest - @Feature({"Gamepad"}) - public void testShieldGamepadMappings() throws Exception { - GamepadMappings.mapToStandardGamepad(mMappedAxes, mMappedButtons, mRawAxes, mRawButtons, - GamepadMappings.NVIDIA_SHIELD_DEVICE_NAME_PREFIX); - - assertShieldGamepadMappings(); - } - - @SmallTest - @Feature({"Gamepad"}) - public void testXBox360GamepadMappings() throws Exception { - GamepadMappings.mapToStandardGamepad(mMappedAxes, mMappedButtons, mRawAxes, mRawButtons, - GamepadMappings.MICROSOFT_XBOX_PAD_DEVICE_NAME); - - assertShieldGamepadMappings(); - } - - @SmallTest - @Feature({"Gamepad"}) - public void testPS3SixAxisGamepadMappings() throws Exception { - GamepadMappings.mapToStandardGamepad(mMappedAxes, mMappedButtons, mRawAxes, mRawButtons, - GamepadMappings.PS3_SIXAXIS_DEVICE_NAME); - - assertEquals(mMappedButtons[CanonicalButtonIndex.PRIMARY], - mRawButtons[KeyEvent.KEYCODE_BUTTON_X]); - assertEquals(mMappedButtons[CanonicalButtonIndex.SECONDARY], - mRawButtons[KeyEvent.KEYCODE_BUTTON_Y]); - assertEquals(mMappedButtons[CanonicalButtonIndex.TERTIARY], - mRawButtons[KeyEvent.KEYCODE_BUTTON_A]); - assertEquals(mMappedButtons[CanonicalButtonIndex.QUATERNARY], - mRawButtons[KeyEvent.KEYCODE_BUTTON_B]); - - assertMappedCommonTriggerButtons(); - assertMappedCommonThumbstickButtons(); - assertMappedCommonDpadButtons(); - assertMappedCommonStartSelectMetaButtons(); - assertMappedTriggerAxexToShoulderButtons(); - assertMappedXYAxes(); - assertMappedZAndRZAxesToRightStick(); - - assertMapping(); - } - - @SmallTest - @Feature({"Gamepad"}) - public void testSamsungEIGP20GamepadMappings() throws Exception { - GamepadMappings.mapToStandardGamepad(mMappedAxes, mMappedButtons, mRawAxes, mRawButtons, - GamepadMappings.SAMSUNG_EI_GP20_DEVICE_NAME); - - assertMappedCommonXYABButtons(); - assertMappedCommonTriggerButtons(); - assertMappedCommonThumbstickButtons(); - assertMappedCommonStartSelectMetaButtons(); - assertMappedHatAxisToDpadButtons(); - assertMappedXYAxes(); - assertMappedRXAndRYAxesToRightStick(); - - expectNoShoulderButtons(); - assertMapping(); - } - - @SmallTest - @Feature({"Gamepad"}) - public void testAmazonFireGamepadMappings() throws Exception { - GamepadMappings.mapToStandardGamepad(mMappedAxes, mMappedButtons, mRawAxes, mRawButtons, - GamepadMappings.AMAZON_FIRE_DEVICE_NAME); - - assertMappedCommonXYABButtons(); - assertMappedPedalAxesToBottomShoulder(); - assertMappedCommonThumbstickButtons(); - assertMappedCommonStartSelectMetaButtons(); - assertMappedTriggerButtonsToTopShoulder(); - assertMappedHatAxisToDpadButtons(); - assertMappedXYAxes(); - assertMappedZAndRZAxesToRightStick(); - - assertMapping(); - } - - @SmallTest - @Feature({"Gamepad"}) - public void testUnknownGamepadMappings() throws Exception { - GamepadMappings.mapToStandardGamepad( - mMappedAxes, mMappedButtons, mRawAxes, mRawButtons, ""); - - assertMappedCommonXYABButtons(); - assertMappedCommonTriggerButtons(); - assertMappedCommonThumbstickButtons(); - assertMappedCommonStartSelectMetaButtons(); - assertMappedTriggerAxexToShoulderButtons(); - assertMappedCommonDpadButtons(); - assertMappedXYAxes(); - assertMappedRXAndRYAxesToRightStick(); - - assertMapping(); - } - - /** - * Asserts that the current gamepad mapping being tested matches the shield mappings. - */ - public void assertShieldGamepadMappings() { - assertMappedCommonXYABButtons(); - assertMappedTriggerButtonsToTopShoulder(); - assertMappedCommonThumbstickButtons(); - assertMappedCommonStartSelectMetaButtons(); - assertMappedTriggerAxesToBottomShoulder(); - assertMappedHatAxisToDpadButtons(); - assertMappedXYAxes(); - assertMappedZAndRZAxesToRightStick(); - - assertMapping(); - } - - public void expectNoShoulderButtons() { - mUnmappedButtons.set(CanonicalButtonIndex.LEFT_SHOULDER); - mUnmappedButtons.set(CanonicalButtonIndex.RIGHT_SHOULDER); - } - - public void assertMapping() { - for (int i = 0; i < mMappedAxes.length; i++) { - if (mUnmappedAxes.get(i)) { - assertTrue( - "An unexpected axis was mapped at index " + i, Float.isNaN(mMappedAxes[i])); - } else { - assertFalse("An axis was not mapped at index " + i, Float.isNaN(mMappedAxes[i])); - } - } - for (int i = 0; i < mMappedButtons.length; i++) { - if (mUnmappedButtons.get(i)) { - assertTrue("An unexpected button was mapped at index " + i, - Float.isNaN(mMappedButtons[i])); - } else { - assertFalse( - "A button was not mapped at index " + i, Float.isNaN(mMappedButtons[i])); - } - } - } - - private void assertMappedCommonTriggerButtons() { - assertEquals(mMappedButtons[CanonicalButtonIndex.LEFT_TRIGGER], - mRawButtons[KeyEvent.KEYCODE_BUTTON_L1]); - assertEquals(mMappedButtons[CanonicalButtonIndex.RIGHT_TRIGGER], - mRawButtons[KeyEvent.KEYCODE_BUTTON_R1]); - } - - private void assertMappedCommonDpadButtons() { - assertEquals(mMappedButtons[CanonicalButtonIndex.DPAD_DOWN], - mRawButtons[KeyEvent.KEYCODE_DPAD_DOWN]); - assertEquals(mMappedButtons[CanonicalButtonIndex.DPAD_UP], - mRawButtons[KeyEvent.KEYCODE_DPAD_UP]); - assertEquals(mMappedButtons[CanonicalButtonIndex.DPAD_LEFT], - mRawButtons[KeyEvent.KEYCODE_DPAD_LEFT]); - assertEquals(mMappedButtons[CanonicalButtonIndex.DPAD_RIGHT], - mRawButtons[KeyEvent.KEYCODE_DPAD_RIGHT]); - } - - private void assertMappedTriggerAxexToShoulderButtons() { - assertEquals(mMappedButtons[CanonicalButtonIndex.LEFT_SHOULDER], - mRawAxes[MotionEvent.AXIS_LTRIGGER]); - assertEquals(mMappedButtons[CanonicalButtonIndex.RIGHT_SHOULDER], - mRawAxes[MotionEvent.AXIS_RTRIGGER]); - } - - private void assertMappedTriggerButtonsToTopShoulder() { - assertEquals(mMappedButtons[CanonicalButtonIndex.LEFT_SHOULDER], - mRawButtons[KeyEvent.KEYCODE_BUTTON_L1]); - assertEquals(mMappedButtons[CanonicalButtonIndex.RIGHT_SHOULDER], - mRawButtons[KeyEvent.KEYCODE_BUTTON_R1]); - } - - private void assertMappedCommonXYABButtons() { - assertEquals(mMappedButtons[CanonicalButtonIndex.PRIMARY], - mRawButtons[KeyEvent.KEYCODE_BUTTON_A]); - assertEquals(mMappedButtons[CanonicalButtonIndex.SECONDARY], - mRawButtons[KeyEvent.KEYCODE_BUTTON_B]); - assertEquals(mMappedButtons[CanonicalButtonIndex.TERTIARY], - mRawButtons[KeyEvent.KEYCODE_BUTTON_X]); - assertEquals(mMappedButtons[CanonicalButtonIndex.QUATERNARY], - mRawButtons[KeyEvent.KEYCODE_BUTTON_Y]); - } - - private void assertMappedCommonThumbstickButtons() { - assertEquals(mMappedButtons[CanonicalButtonIndex.LEFT_THUMBSTICK], - mRawButtons[KeyEvent.KEYCODE_BUTTON_THUMBL]); - assertEquals(mMappedButtons[CanonicalButtonIndex.RIGHT_THUMBSTICK], - mRawButtons[KeyEvent.KEYCODE_BUTTON_THUMBR]); - } - - private void assertMappedCommonStartSelectMetaButtons() { - assertEquals(mMappedButtons[CanonicalButtonIndex.START], - mRawButtons[KeyEvent.KEYCODE_BUTTON_START]); - assertEquals(mMappedButtons[CanonicalButtonIndex.BACK_SELECT], - mRawButtons[KeyEvent.KEYCODE_BUTTON_SELECT]); - assertEquals(mMappedButtons[CanonicalButtonIndex.META], - mRawButtons[KeyEvent.KEYCODE_BUTTON_MODE]); - } - - private void assertMappedPedalAxesToBottomShoulder() { - assertEquals(mMappedButtons[CanonicalButtonIndex.LEFT_TRIGGER], - mRawAxes[MotionEvent.AXIS_BRAKE]); - assertEquals( - mMappedButtons[CanonicalButtonIndex.RIGHT_TRIGGER], mRawAxes[MotionEvent.AXIS_GAS]); - } - - private void assertMappedTriggerAxesToBottomShoulder() { - assertEquals(mMappedButtons[CanonicalButtonIndex.LEFT_TRIGGER], - mRawAxes[MotionEvent.AXIS_LTRIGGER]); - assertEquals(mMappedButtons[CanonicalButtonIndex.RIGHT_TRIGGER], - mRawAxes[MotionEvent.AXIS_RTRIGGER]); - } - - private void assertMappedHatAxisToDpadButtons() { - float hatX = mRawAxes[MotionEvent.AXIS_HAT_X]; - float hatY = mRawAxes[MotionEvent.AXIS_HAT_Y]; - assertEquals(mMappedButtons[CanonicalButtonIndex.DPAD_LEFT], - GamepadMappings.negativeAxisValueAsButton(hatX)); - assertEquals(mMappedButtons[CanonicalButtonIndex.DPAD_RIGHT], - GamepadMappings.positiveAxisValueAsButton(hatX)); - assertEquals(mMappedButtons[CanonicalButtonIndex.DPAD_UP], - GamepadMappings.negativeAxisValueAsButton(hatY)); - assertEquals(mMappedButtons[CanonicalButtonIndex.DPAD_DOWN], - GamepadMappings.positiveAxisValueAsButton(hatY)); - } - - private void assertMappedXYAxes() { - assertEquals(mMappedAxes[CanonicalAxisIndex.LEFT_STICK_X], mRawAxes[MotionEvent.AXIS_X]); - assertEquals(mMappedAxes[CanonicalAxisIndex.LEFT_STICK_Y], mRawAxes[MotionEvent.AXIS_Y]); - } - - private void assertMappedRXAndRYAxesToRightStick() { - assertEquals(mMappedAxes[CanonicalAxisIndex.RIGHT_STICK_X], mRawAxes[MotionEvent.AXIS_RX]); - assertEquals(mMappedAxes[CanonicalAxisIndex.RIGHT_STICK_Y], mRawAxes[MotionEvent.AXIS_RY]); - } - - private void assertMappedZAndRZAxesToRightStick() { - assertEquals(mMappedAxes[CanonicalAxisIndex.RIGHT_STICK_X], mRawAxes[MotionEvent.AXIS_Z]); - assertEquals(mMappedAxes[CanonicalAxisIndex.RIGHT_STICK_Y], mRawAxes[MotionEvent.AXIS_RZ]); - } -}
diff --git a/content/public/browser/push_messaging_service.cc b/content/public/browser/push_messaging_service.cc index 061af7ad..27335eb 100644 --- a/content/public/browser/push_messaging_service.cc +++ b/content/public/browser/push_messaging_service.cc
@@ -50,7 +50,7 @@ const std::string& key, const PushMessagingService::StringCallback& callback) { DCHECK_CURRENTLY_ON(BrowserThread::IO); - service_worker_context_wrapper->context()->storage()->GetUserData( + service_worker_context_wrapper->GetRegistrationUserData( service_worker_registration_id, key, base::Bind(&CallStringCallbackFromIO, callback)); } @@ -61,7 +61,7 @@ const std::string& data, const PushMessagingService::ResultCallback& callback) { DCHECK_CURRENTLY_ON(BrowserThread::IO); - service_worker_context_wrapper->context()->storage()->StoreUserData( + service_worker_context_wrapper->StoreRegistrationUserData( service_worker_registration_id, origin, kNotificationsShownServiceWorkerKey, data, base::Bind(&CallResultCallbackFromIO, callback)); @@ -73,7 +73,7 @@ const base::Closure& callback) { DCHECK_CURRENTLY_ON(BrowserThread::IO); - service_worker_context->context()->storage()->ClearUserData( + service_worker_context->ClearRegistrationUserData( service_worker_registration_id, kPushRegistrationIdServiceWorkerKey, base::Bind(&CallClosureFromIO, callback));
diff --git a/content/public/browser/ssl_host_state_delegate.h b/content/public/browser/ssl_host_state_delegate.h index 4666471..39589a5 100644 --- a/content/public/browser/ssl_host_state_delegate.h +++ b/content/public/browser/ssl_host_state_delegate.h
@@ -54,6 +54,16 @@ virtual bool DidHostRunInsecureContent(const std::string& host, int pid) const = 0; + // Revokes all SSL certificate error allow exceptions made by the user for + // |host|. + virtual void RevokeUserAllowExceptions(const std::string& host) = 0; + + // Returns whether the user has allowed a certificate error exception for + // |host|. This does not mean that *all* certificate errors are allowed, just + // that there exists an exception. To see if a particular certificate and + // error combination exception is allowed, use QueryPolicy(). + virtual bool HasAllowException(const std::string& host) const = 0; + protected: virtual ~SSLHostStateDelegate() {} };
diff --git a/content/public/common/content_switches.cc b/content/public/common/content_switches.cc index f9b5801..6672723 100644 --- a/content/public/common/content_switches.cc +++ b/content/public/common/content_switches.cc
@@ -91,10 +91,6 @@ // features. const char kDisableBlinkFeatures[] = "disable-blink-features"; -// Disable the Blink Scheduler. Ensures there's no reordering of blink tasks. -// This switch is intended only for performance tests. -const char kDisableBlinkScheduler[] = "disable-blink-scheduler"; - // Disable the creation of compositing layers when it would prevent LCD text. const char kDisablePreferCompositingToLCDText[] = "disable-prefer-compositing-to-lcd-text"; @@ -907,12 +903,6 @@ // Override the maximum framerate as can be specified in calls to getUserMedia. // This flag expects a value. Example: --max-gum-fps=17.5 const char kWebRtcMaxCaptureFramerate[] = "max-gum-fps"; - -#if defined(OS_LINUX) || defined(OS_MACOSX) -// Enables Video Capture Device captured data uploaded to Texture for zero-copy -// capture pipeline. -const char kEnableWebRtcCaptureToTexture[] = "enable-webrtc-capture-to-texture"; -#endif #endif #if defined(OS_ANDROID)
diff --git a/content/public/common/content_switches.h b/content/public/common/content_switches.h index c72322a..a183e70e 100644 --- a/content/public/common/content_switches.h +++ b/content/public/common/content_switches.h
@@ -36,7 +36,6 @@ CONTENT_EXPORT extern const char kDisableAcceleratedJpegDecoding[]; CONTENT_EXPORT extern const char kDisableAcceleratedVideoDecode[]; extern const char kDisableBackingStoreLimit[]; -CONTENT_EXPORT extern const char kDisableBlinkScheduler[]; CONTENT_EXPORT extern const char kDisablePreferCompositingToLCDText[]; CONTENT_EXPORT extern const char kDisableDatabases[]; CONTENT_EXPORT extern const char kDisableDelegatedRenderer[]; @@ -252,9 +251,6 @@ CONTENT_EXPORT extern const char kEnableWebRtcHWH264Encoding[]; CONTENT_EXPORT extern const char kEnableWebRtcStunOrigin[]; extern const char kWebRtcMaxCaptureFramerate[]; -#if defined(OS_LINUX) || defined(OS_MACOSX) -CONTENT_EXPORT extern const char kEnableWebRtcCaptureToTexture[]; -#endif #endif #if defined(OS_ANDROID)
diff --git a/content/public/common/speech_recognition_error.h b/content/public/common/speech_recognition_error.h index bbd73c7..7456a43 100644 --- a/content/public/common/speech_recognition_error.h +++ b/content/public/common/speech_recognition_error.h
@@ -14,11 +14,14 @@ // There was no error. SPEECH_RECOGNITION_ERROR_NONE, + // No speech heard before timeout. + SPEECH_RECOGNITION_ERROR_NO_SPEECH, + // The user or a script aborted speech input. SPEECH_RECOGNITION_ERROR_ABORTED, // There was an error with recording audio. - SPEECH_RECOGNITION_ERROR_AUDIO, + SPEECH_RECOGNITION_ERROR_AUDIO_CAPTURE, // There was a network error. SPEECH_RECOGNITION_ERROR_NETWORK, @@ -26,15 +29,19 @@ // Not allowed for privacy or security reasons. SPEECH_RECOGNITION_ERROR_NOT_ALLOWED, - // No speech heard before timeout. - SPEECH_RECOGNITION_ERROR_NO_SPEECH, + // Speech service is not allowed for privacy or security reasons. + SPEECH_RECOGNITION_ERROR_SERVICE_NOT_ALLOWED, + + // There was an error in the speech recognition grammar. + SPEECH_RECOGNITION_ERROR_BAD_GRAMMAR, + + // The language was not supported. + SPEECH_RECOGNITION_ERROR_LANGUAGE_NOT_SUPPORTED, // Speech was heard, but could not be interpreted. SPEECH_RECOGNITION_ERROR_NO_MATCH, - // There was an error in the speech recognition grammar. - SPEECH_RECOGNITION_ERROR_BAD_GRAMMAR, - SPEECH_RECOGNITION_ERROR_LAST = SPEECH_RECOGNITION_ERROR_BAD_GRAMMAR, + SPEECH_RECOGNITION_ERROR_LAST = SPEECH_RECOGNITION_ERROR_NO_MATCH, }; // Error details for the SPEECH_RECOGNITION_ERROR_AUDIO error.
diff --git a/content/public/test/DEPS b/content/public/test/DEPS index e897ad3..fbe84ed7 100644 --- a/content/public/test/DEPS +++ b/content/public/test/DEPS
@@ -7,6 +7,10 @@ # Ensure we don't leak internal content headers through public headers. specific_include_rules = { ".*\.cc": [ + # Allow inclusion of specific components that we depend on. We may only + # depend on components which we share with the mojo html_viewer. + "+components/scheduler/renderer", + # Testing utilities can access anything in content/ "+content", "+gin/v8_initializer.h",
diff --git a/content/public/test/android/javatests/src/org/chromium/content/browser/test/util/CriteriaHelper.java b/content/public/test/android/javatests/src/org/chromium/content/browser/test/util/CriteriaHelper.java index 6142ffb..3316c7b 100644 --- a/content/public/test/android/javatests/src/org/chromium/content/browser/test/util/CriteriaHelper.java +++ b/content/public/test/android/javatests/src/org/chromium/content/browser/test/util/CriteriaHelper.java
@@ -61,15 +61,19 @@ } /** - * Checks whether the given Criteria is satisfied polling at a default interval on the UI - * thread. + * Checks whether the given Criteria is satisfied polling at a given interval on the UI + * thread, until either the criteria is satisfied, or the maxTimeoutMs number of ms has elapsed. + * * @param criteria The Criteria that will be checked. + * @param maxTimeoutMs The maximum number of ms that this check will be performed for + * before timeout. + * @param checkIntervalMs The number of ms between checks. * @return iff checking has ended with the criteria being satisfied. * @throws InterruptedException * @see #pollForCriteria(Criteria) */ - public static boolean pollForUIThreadCriteria(final Criteria criteria) - throws InterruptedException { + public static boolean pollForUIThreadCriteria(final Criteria criteria, long maxTimeoutMs, + long checkIntervalMs) throws InterruptedException { final Callable<Boolean> callable = new Callable<Boolean>() { @Override public Boolean call() throws Exception { @@ -82,7 +86,21 @@ public boolean isSatisfied() { return ThreadUtils.runOnUiThreadBlockingNoException(callable); } - }); + }, maxTimeoutMs, checkIntervalMs); + } + + /** + * Checks whether the given Criteria is satisfied polling at a default interval on the UI + * thread. + * @param criteria The Criteria that will be checked. + * @return iff checking has ended with the criteria being satisfied. + * @throws InterruptedException + * @see #pollForCriteria(Criteria) + */ + public static boolean pollForUIThreadCriteria(final Criteria criteria) + throws InterruptedException { + return pollForUIThreadCriteria(criteria, DEFAULT_MAX_TIME_TO_POLL, + DEFAULT_POLLING_INTERVAL); } /**
diff --git a/content/public/test/render_view_test.cc b/content/public/test/render_view_test.cc index ca67587..1e1a048 100644 --- a/content/public/test/render_view_test.cc +++ b/content/public/test/render_view_test.cc
@@ -5,6 +5,7 @@ #include "content/public/test/render_view_test.h" #include "base/run_loop.h" +#include "components/scheduler/renderer/renderer_scheduler.h" #include "content/common/dom_storage/dom_storage_types.h" #include "content/common/frame_messages.h" #include "content/common/input_messages.h" @@ -21,7 +22,6 @@ #include "content/renderer/render_view_impl.h" #include "content/renderer/renderer_blink_platform_impl.h" #include "content/renderer/renderer_main_platform_delegate.h" -#include "content/renderer/scheduler/renderer_scheduler.h" #include "content/test/fake_compositor_dependencies.h" #include "content/test/mock_render_process.h" #include "content/test/test_content_client.h" @@ -63,7 +63,8 @@ class RendererBlinkPlatformImplNoSandboxImpl : public RendererBlinkPlatformImpl { public: - RendererBlinkPlatformImplNoSandboxImpl(RendererScheduler* scheduler) + RendererBlinkPlatformImplNoSandboxImpl( + scheduler::RendererScheduler* scheduler) : RendererBlinkPlatformImpl(scheduler) {} virtual blink::WebSandboxSupport* sandboxSupport() { @@ -73,7 +74,7 @@ RenderViewTest::RendererBlinkPlatformImplNoSandbox:: RendererBlinkPlatformImplNoSandbox() { - renderer_scheduler_ = RendererScheduler::Create(); + renderer_scheduler_ = scheduler::RendererScheduler::Create(); blink_platform_impl_.reset( new RendererBlinkPlatformImplNoSandboxImpl(renderer_scheduler_.get())); }
diff --git a/content/public/test/render_view_test.h b/content/public/test/render_view_test.h index d512c54..d3fd776e 100644 --- a/content/public/test/render_view_test.h +++ b/content/public/test/render_view_test.h
@@ -29,6 +29,10 @@ class Rect; } +namespace scheduler { +class RendererScheduler; +} + namespace content { class ContentBrowserClient; class ContentClient; @@ -38,7 +42,6 @@ class PageState; class RendererMainPlatformDelegate; class RendererBlinkPlatformImplNoSandboxImpl; -class RendererScheduler; class RenderView; class RenderViewTest : public testing::Test { @@ -52,7 +55,7 @@ blink::Platform* Get(); private: - scoped_ptr<RendererScheduler> renderer_scheduler_; + scoped_ptr<scheduler::RendererScheduler> renderer_scheduler_; scoped_ptr<RendererBlinkPlatformImplNoSandboxImpl> blink_platform_impl_; };
diff --git a/content/renderer/BUILD.gn b/content/renderer/BUILD.gn index 13713fd..d4a17fb 100644 --- a/content/renderer/BUILD.gn +++ b/content/renderer/BUILD.gn
@@ -28,6 +28,7 @@ "//base/allocator", "//cc", "//cc/blink", + "//components/scheduler:scheduler", "//content:resources", "//content/common:mojo_bindings", "//content/public/child:child_sources",
diff --git a/content/renderer/DEPS b/content/renderer/DEPS index 076c1129..1f43f937 100644 --- a/content/renderer/DEPS +++ b/content/renderer/DEPS
@@ -1,4 +1,8 @@ include_rules = [ + # Allow inclusion of specific components that we depend on. We may only + # depend on components which we share with the mojo html_viewer. + "+components/scheduler", + "+cc/blink", "+content/public/child", "+content/public/renderer",
diff --git a/content/renderer/accessibility/blink_ax_tree_source.cc b/content/renderer/accessibility/blink_ax_tree_source.cc index 1ad905da..c0a1ef5 100644 --- a/content/renderer/accessibility/blink_ax_tree_source.cc +++ b/content/renderer/accessibility/blink_ax_tree_source.cc
@@ -335,6 +335,12 @@ src.hierarchicalLevel()); } + if (src.setSize()) + dst->AddIntAttribute(ui::AX_ATTR_SET_SIZE, src.setSize()); + + 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()) {
diff --git a/content/renderer/devtools/v8_sampling_profiler.cc b/content/renderer/devtools/v8_sampling_profiler.cc index 8dc70d9c..20b2238 100644 --- a/content/renderer/devtools/v8_sampling_profiler.cc +++ b/content/renderer/devtools/v8_sampling_profiler.cc
@@ -542,7 +542,11 @@ state.fp = reinterpret_cast<void*>(mcontext.gregs[REG_64_32(REG_RBP, REG_EBP)]); #endif // OS_MACOS -#endif // ARCH_CPU_X86_FAMILY +#elif defined(ARCH_CPU_MIPS_FAMILY) + state.pc = reinterpret_cast<void*>(mcontext.pc); + state.sp = reinterpret_cast<void*>(mcontext.gregs[29]); + state.fp = reinterpret_cast<void*>(mcontext.gregs[30]); +#endif // ARCH_CPU_MIPS_FAMILY Sampler::GetInstance()->DoSample(state); }
diff --git a/content/renderer/gpu/compositor_dependencies.h b/content/renderer/gpu/compositor_dependencies.h index 5c72ee7c..9973a65 100644 --- a/content/renderer/gpu/compositor_dependencies.h +++ b/content/renderer/gpu/compositor_dependencies.h
@@ -23,8 +23,11 @@ class GpuMemoryBufferManager; } -namespace content { +namespace scheduler { class RendererScheduler; +} + +namespace content { class CompositorDependencies { public: @@ -48,7 +51,7 @@ GetCompositorImplThreadTaskRunner() = 0; virtual cc::SharedBitmapManager* GetSharedBitmapManager() = 0; virtual gpu::GpuMemoryBufferManager* GetGpuMemoryBufferManager() = 0; - virtual RendererScheduler* GetRendererScheduler() = 0; + virtual scheduler::RendererScheduler* GetRendererScheduler() = 0; virtual cc::ContextProvider* GetSharedMainThreadContextProvider() = 0; virtual scoped_ptr<cc::BeginFrameSource> CreateExternalBeginFrameSource( int routing_id) = 0;
diff --git a/content/renderer/gpu/gpu_benchmarking_extension.cc b/content/renderer/gpu/gpu_benchmarking_extension.cc index d4890b5..45a350a 100644 --- a/content/renderer/gpu/gpu_benchmarking_extension.cc +++ b/content/renderer/gpu/gpu_benchmarking_extension.cc
@@ -254,11 +254,8 @@ scoped_ptr<V8ValueConverter> converter = make_scoped_ptr(V8ValueConverter::create()); v8::Handle<v8::Value> value = converter->ToV8Value(result.get(), context); -#ifdef WEB_FRAME_USES_V8_LOCAL - v8::Local<v8::Value> argv[] = {value}; -#else v8::Handle<v8::Value> argv[] = { value }; -#endif + frame->callFunctionEvenIfScriptDisabled( callback_and_context->GetCallback(), v8::Object::New(isolate),
diff --git a/content/renderer/gpu/render_widget_compositor.cc b/content/renderer/gpu/render_widget_compositor.cc index 31aa6a8..0264aa5 100644 --- a/content/renderer/gpu/render_widget_compositor.cc +++ b/content/renderer/gpu/render_widget_compositor.cc
@@ -30,11 +30,11 @@ #include "cc/scheduler/begin_frame_source.h" #include "cc/trees/latency_info_swap_promise_monitor.h" #include "cc/trees/layer_tree_host.h" +#include "components/scheduler/renderer/renderer_scheduler.h" #include "content/common/content_switches_internal.h" #include "content/common/gpu/client/context_provider_command_buffer.h" #include "content/public/common/content_switches.h" #include "content/renderer/input/input_handler_manager.h" -#include "content/renderer/scheduler/renderer_scheduler.h" #include "gpu/command_buffer/client/gles2_interface.h" #include "third_party/WebKit/public/platform/WebCompositeAndReadbackAsyncCallback.h" #include "third_party/WebKit/public/platform/WebSelectionBound.h"
diff --git a/content/renderer/gpu/render_widget_compositor_unittest.cc b/content/renderer/gpu/render_widget_compositor_unittest.cc index 448fa1f..6becdf51 100644 --- a/content/renderer/gpu/render_widget_compositor_unittest.cc +++ b/content/renderer/gpu/render_widget_compositor_unittest.cc
@@ -7,9 +7,9 @@ #include "cc/output/begin_frame_args.h" #include "cc/test/failure_output_surface.h" #include "cc/trees/layer_tree_host.h" +#include "components/scheduler/renderer/renderer_scheduler.h" #include "content/public/test/mock_render_thread.h" #include "content/renderer/render_widget.h" -#include "content/renderer/scheduler/renderer_scheduler.h" #include "content/test/fake_compositor_dependencies.h" #include "content/test/fake_renderer_scheduler.h" #include "testing/gmock/include/gmock/gmock.h"
diff --git a/content/renderer/input/input_handler_manager.cc b/content/renderer/input/input_handler_manager.cc index f266cb85..ef011cc 100644 --- a/content/renderer/input/input_handler_manager.cc +++ b/content/renderer/input/input_handler_manager.cc
@@ -8,11 +8,11 @@ #include "base/message_loop/message_loop_proxy.h" #include "base/trace_event/trace_event.h" #include "cc/input/input_handler.h" +#include "components/scheduler/renderer/renderer_scheduler.h" #include "content/renderer/input/input_event_filter.h" #include "content/renderer/input/input_handler_manager_client.h" #include "content/renderer/input/input_handler_wrapper.h" #include "content/renderer/input/input_scroll_elasticity_controller.h" -#include "content/renderer/scheduler/renderer_scheduler.h" using blink::WebInputEvent; @@ -39,7 +39,7 @@ InputHandlerManager::InputHandlerManager( const scoped_refptr<base::MessageLoopProxy>& message_loop_proxy, InputHandlerManagerClient* client, - RendererScheduler* renderer_scheduler) + scheduler::RendererScheduler* renderer_scheduler) : message_loop_proxy_(message_loop_proxy), client_(client), renderer_scheduler_(renderer_scheduler) {
diff --git a/content/renderer/input/input_handler_manager.h b/content/renderer/input/input_handler_manager.h index 130fc97..e49d566 100644 --- a/content/renderer/input/input_handler_manager.h +++ b/content/renderer/input/input_handler_manager.h
@@ -25,12 +25,15 @@ class WebMouseWheelEvent; } +namespace scheduler { +class RendererScheduler; +} + namespace content { class InputHandlerWrapper; class InputHandlerManagerClient; struct DidOverscrollParams; -class RendererScheduler; // InputHandlerManager class manages InputHandlerProxy instances for // the WebViews in this renderer. @@ -43,7 +46,7 @@ InputHandlerManager( const scoped_refptr<base::MessageLoopProxy>& message_loop_proxy, InputHandlerManagerClient* client, - RendererScheduler* renderer_scheduler); + scheduler::RendererScheduler* renderer_scheduler); ~InputHandlerManager(); // Callable from the main thread only. @@ -96,7 +99,7 @@ scoped_refptr<base::MessageLoopProxy> message_loop_proxy_; InputHandlerManagerClient* client_; - RendererScheduler* renderer_scheduler_; // Not owned. + scheduler::RendererScheduler* renderer_scheduler_; // Not owned. }; } // namespace content
diff --git a/content/renderer/media/media_stream_audio_processor.cc b/content/renderer/media/media_stream_audio_processor.cc index af9824d..78237d98c 100644 --- a/content/renderer/media/media_stream_audio_processor.cc +++ b/content/renderer/media/media_stream_audio_processor.cc
@@ -85,10 +85,17 @@ } bool IsBeamformingEnabled(const MediaAudioConstraints& audio_constraints) { - return audio_constraints.GetProperty( - MediaAudioConstraints::kGoogBeamforming) || - base::FieldTrialList::FindFullName("ChromebookBeamforming") == - "Enabled"; + return base::FieldTrialList::FindFullName("ChromebookBeamforming") == + "Enabled" || + audio_constraints.GetProperty(MediaAudioConstraints::kGoogBeamforming); +} + +bool IsAudioProcessing48kHzSupportEnabled( + const MediaAudioConstraints& audio_constraints) { + return base::FieldTrialList::FindFullName("AudioProcessing48kHzSupport") == + "Enabled" || + audio_constraints.GetProperty( + MediaAudioConstraints::kGoogAudioProcessing48kHzSupport); } } // namespace @@ -465,8 +472,8 @@ const bool goog_beamforming = IsBeamformingEnabled(audio_constraints); const bool goog_high_pass_filter = audio_constraints.GetProperty( MediaAudioConstraints::kGoogHighpassFilter); - audio_proc_48kHz_support_ = audio_constraints.GetProperty( - MediaAudioConstraints::kGoogAudioProcessing48kHzSupport); + audio_proc_48kHz_support_ = + IsAudioProcessing48kHzSupportEnabled(audio_constraints); // Return immediately if no goog constraint is enabled. if (!echo_cancellation && !goog_experimental_aec && !goog_ns && !goog_high_pass_filter && !goog_typing_detection &&
diff --git a/content/renderer/media/render_media_client_unittest.cc b/content/renderer/media/render_media_client_unittest.cc index 7ac253e..f40d0c5 100644 --- a/content/renderer/media/render_media_client_unittest.cc +++ b/content/renderer/media/render_media_client_unittest.cc
@@ -30,13 +30,13 @@ key_system_info.max_audio_robustness = media::EmeRobustness::EMPTY; key_system_info.max_video_robustness = media::EmeRobustness::EMPTY; key_system_info.persistent_license_support = - media::EME_SESSION_TYPE_NOT_SUPPORTED; + media::EmeSessionTypeSupport::NOT_SUPPORTED; key_system_info.persistent_release_message_support = - media::EME_SESSION_TYPE_NOT_SUPPORTED; + media::EmeSessionTypeSupport::NOT_SUPPORTED; key_system_info.persistent_state_support = - media::EME_FEATURE_NOT_SUPPORTED; + media::EmeFeatureSupport::NOT_SUPPORTED; key_system_info.distinctive_identifier_support = - media::EME_FEATURE_NOT_SUPPORTED; + media::EmeFeatureSupport::NOT_SUPPORTED; key_systems_info->push_back(key_system_info); #if defined(WIDEVINE_CDM_AVAILABLE) && defined(WIDEVINE_CDM_IS_COMPONENT) if (is_extra_key_system_enabled_) { @@ -45,13 +45,13 @@ wv_key_system_info.max_audio_robustness = media::EmeRobustness::EMPTY; wv_key_system_info.max_video_robustness = media::EmeRobustness::EMPTY; wv_key_system_info.persistent_license_support = - media::EME_SESSION_TYPE_NOT_SUPPORTED; + media::EmeSessionTypeSupport::NOT_SUPPORTED; wv_key_system_info.persistent_release_message_support = - media::EME_SESSION_TYPE_NOT_SUPPORTED; + media::EmeSessionTypeSupport::NOT_SUPPORTED; wv_key_system_info.persistent_state_support = - media::EME_FEATURE_NOT_SUPPORTED; + media::EmeFeatureSupport::NOT_SUPPORTED; wv_key_system_info.distinctive_identifier_support = - media::EME_FEATURE_NOT_SUPPORTED; + media::EmeFeatureSupport::NOT_SUPPORTED; key_systems_info->push_back(wv_key_system_info); } #endif
diff --git a/content/renderer/pepper/pepper_plugin_instance_impl.cc b/content/renderer/pepper/pepper_plugin_instance_impl.cc index 07930b3..00e2f05 100644 --- a/content/renderer/pepper/pepper_plugin_instance_impl.cc +++ b/content/renderer/pepper/pepper_plugin_instance_impl.cc
@@ -367,11 +367,11 @@ const ui::LatencyInfo* old_latency, blink::WebInputEvent::Type type, int64 input_sequence) { - new_latency->AddLatencyNumber( + new_latency->AddLatencyNumberWithTraceName( ui::INPUT_EVENT_LATENCY_BEGIN_PLUGIN_COMPONENT, 0, - input_sequence); - new_latency->TraceEventType(WebInputEventTraits::GetName(type)); + input_sequence, + WebInputEventTraits::GetName(type)); if (old_latency) { new_latency->CopyLatencyFrom(*old_latency, ui::INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT);
diff --git a/content/renderer/pepper/ppb_var_deprecated_impl.cc b/content/renderer/pepper/ppb_var_deprecated_impl.cc index 064aeb1..589981cf 100644 --- a/content/renderer/pepper/ppb_var_deprecated_impl.cc +++ b/content/renderer/pepper/ppb_var_deprecated_impl.cc
@@ -248,14 +248,8 @@ return PP_MakeUndefined(); } -#ifdef WEB_FRAME_USES_V8_LOCAL - scoped_ptr<v8::Local<v8::Value>[]> converted_args( - new v8::Local<v8::Value>[argc]); -#else scoped_ptr<v8::Handle<v8::Value>[] > converted_args( new v8::Handle<v8::Value>[argc]); -#endif - for (uint32_t i = 0; i < argc; ++i) { converted_args[i] = try_catch.ToV8(argv[i]); if (try_catch.HasException())
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc index 3cf9e9b8..1e7ead7 100644 --- a/content/renderer/render_frame_impl.cc +++ b/content/renderer/render_frame_impl.cc
@@ -1100,6 +1100,20 @@ << request_params.frame_to_navigate; } + // If this frame isn't in the same process as its parent, it will naively + // assume that this is the first navigation in the iframe, but this may not + // actually be the case. The PageTransition differentiates between the first + // navigation in a subframe and subsequent navigations, so if this is a + // subsequent navigation, force the frame's state machine forward. + if (ui::PageTransitionCoreTypeIs(common_params.transition, + ui::PAGE_TRANSITION_MANUAL_SUBFRAME)) { + CHECK(frame_->parent()); + if (frame_->parent()->isWebRemoteFrame()) { + CHECK_EQ(frame, frame_); + frame_->setCommittedFirstRealLoad(); + } + } + if (is_reload && !render_view_->history_controller()->GetCurrentEntry()) { // We cannot reload if we do not have any history state. This happens, for // example, when recovering from a crash. @@ -2554,6 +2568,8 @@ document_state->set_start_load_time(Time::Now()); bool is_top_most = !frame->parent(); + NavigationStateImpl* navigation_state = + static_cast<NavigationStateImpl*>(document_state->navigation_state()); if (is_top_most) { render_view_->set_navigation_gesture( WebUserGestureIndicator::isProcessingUserGesture() ? @@ -2562,8 +2578,17 @@ // Subframe navigations that don't add session history items must be // marked with AUTO_SUBFRAME. See also didFailProvisionalLoad for how we // handle loading of error pages. - static_cast<NavigationStateImpl*>(document_state->navigation_state()) - ->set_transition_type(ui::PAGE_TRANSITION_AUTO_SUBFRAME); + navigation_state->set_transition_type(ui::PAGE_TRANSITION_AUTO_SUBFRAME); + } else if (ui::PageTransitionCoreTypeIs(navigation_state->GetTransitionType(), + ui::PAGE_TRANSITION_LINK)) { + // Subframe navigations that are creating a new history item should be + // marked MANUAL_SUBFRAME, unless it has already been marked as a + // FORM_SUBMIT. This state will be attached to a main resource request + // in the process that began the request. If the request is transferred + // to a different process, this state will be used in + // RenderFrameImpl::OnNavigate() in the new process (as well as in the + // browser process). + navigation_state->set_transition_type(ui::PAGE_TRANSITION_MANUAL_SUBFRAME); } FOR_EACH_OBSERVER(RenderViewObserver, render_view_->observers(), @@ -3147,16 +3172,9 @@ } } - WebFrame* top_frame = frame->top(); - // TODO(nasko): Hack around asking about top-frame data source. This means - // for out-of-process iframes we are treating the current frame as the - // top-level frame, which is wrong. - if (!top_frame || top_frame->isWebRemoteFrame()) - top_frame = frame; - WebDataSource* provisional_data_source = top_frame->provisionalDataSource(); - WebDataSource* top_data_source = top_frame->dataSource(); + WebDataSource* provisional_data_source = frame->provisionalDataSource(); WebDataSource* data_source = - provisional_data_source ? provisional_data_source : top_data_source; + provisional_data_source ? provisional_data_source : frame->dataSource(); DocumentState* document_state = DocumentState::FromDataSource(data_source); DCHECK(document_state); @@ -3295,8 +3313,14 @@ extra_data->set_stream_override(stream_override.Pass()); request.setExtraData(extra_data); + WebFrame* top_frame = frame->top(); + // TODO(nasko): Hack around asking about top-frame data source. This means + // for out-of-process iframes we are treating the current frame as the + // top-level frame, which is wrong. + if (!top_frame || top_frame->isWebRemoteFrame()) + top_frame = frame; DocumentState* top_document_state = - DocumentState::FromDataSource(top_data_source); + DocumentState::FromDataSource(top_frame->dataSource()); if (top_document_state) { // TODO(gavinp): separate out prefetching and prerender field trials // if the rel=prerender rel type is sticking around.
diff --git a/content/renderer/render_thread_impl.cc b/content/renderer/render_thread_impl.cc index 7f03f4e..61fd840 100644 --- a/content/renderer/render_thread_impl.cc +++ b/content/renderer/render_thread_impl.cc
@@ -33,6 +33,7 @@ #include "cc/blink/web_external_bitmap_impl.h" #include "cc/blink/web_layer_impl.h" #include "cc/resources/tile_task_worker_pool.h" +#include "components/scheduler/renderer/renderer_scheduler.h" #include "content/child/appcache/appcache_dispatcher.h" #include "content/child/appcache/appcache_frontend_impl.h" #include "content/child/child_discardable_shared_memory_manager.h" @@ -104,7 +105,6 @@ #include "content/renderer/render_process_impl.h" #include "content/renderer/render_view_impl.h" #include "content/renderer/renderer_blink_platform_impl.h" -#include "content/renderer/scheduler/renderer_scheduler.h" #include "content/renderer/scheduler/resource_dispatch_throttler.h" #include "content/renderer/service_worker/embedded_worker_context_message_filter.h" #include "content/renderer/service_worker/embedded_worker_dispatcher.h" @@ -502,7 +502,7 @@ dom_storage_dispatcher_.reset(new DomStorageDispatcher()); main_thread_indexed_db_dispatcher_.reset(new IndexedDBDispatcher( thread_safe_sender())); - renderer_scheduler_ = RendererScheduler::Create(); + renderer_scheduler_ = scheduler::RendererScheduler::Create(); channel()->SetListenerTaskRunner(renderer_scheduler_->DefaultTaskRunner()); main_thread_cache_storage_dispatcher_.reset( new CacheStorageDispatcher(thread_safe_sender())); @@ -1195,6 +1195,7 @@ --idle_notifications_to_skip_; } else { base::allocator::ReleaseFreeMemory(); + discardable_shared_memory_manager()->ReleaseFreeMemory(); } ScheduleIdleHandler(kLongIdleHandlerDelayMs); return; @@ -1442,7 +1443,7 @@ return gpu_memory_buffer_manager(); } -RendererScheduler* RenderThreadImpl::GetRendererScheduler() { +scheduler::RendererScheduler* RenderThreadImpl::GetRendererScheduler() { return renderer_scheduler_.get(); } @@ -1765,6 +1766,7 @@ void RenderThreadImpl::OnMemoryPressure( base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level) { base::allocator::ReleaseFreeMemory(); + discardable_shared_memory_manager()->ReleaseFreeMemory(); // Do not call into blink if it is not initialized. if (blink_platform_impl_) {
diff --git a/content/renderer/render_thread_impl.h b/content/renderer/render_thread_impl.h index f897fcc..60d4a49 100644 --- a/content/renderer/render_thread_impl.h +++ b/content/renderer/render_thread_impl.h
@@ -70,6 +70,10 @@ class GpuVideoAcceleratorFactories; } +namespace scheduler { +class RendererScheduler; +} + namespace v8 { class Extension; } @@ -102,7 +106,6 @@ class RenderProcessObserver; class RendererBlinkPlatformImpl; class RendererDemuxerAndroid; -class RendererScheduler; class ResourceDispatchThrottler; class ResourceSchedulingFilter; class V8SamplingProfiler; @@ -197,7 +200,7 @@ scoped_refptr<base::SingleThreadTaskRunner> GetCompositorImplThreadTaskRunner() override; gpu::GpuMemoryBufferManager* GetGpuMemoryBufferManager() override; - RendererScheduler* GetRendererScheduler() override; + scheduler::RendererScheduler* GetRendererScheduler() override; cc::ContextProvider* GetSharedMainThreadContextProvider() override; scoped_ptr<cc::BeginFrameSource> CreateExternalBeginFrameSource( int routing_id) override; @@ -480,7 +483,7 @@ scoped_ptr<AppCacheDispatcher> appcache_dispatcher_; scoped_ptr<DomStorageDispatcher> dom_storage_dispatcher_; scoped_ptr<IndexedDBDispatcher> main_thread_indexed_db_dispatcher_; - scoped_ptr<RendererScheduler> renderer_scheduler_; + scoped_ptr<scheduler::RendererScheduler> renderer_scheduler_; scoped_ptr<RendererBlinkPlatformImpl> blink_platform_impl_; scoped_ptr<ResourceDispatchThrottler> resource_dispatch_throttler_; scoped_ptr<CacheStorageDispatcher> main_thread_cache_storage_dispatcher_;
diff --git a/content/renderer/renderer_blink_platform_impl.cc b/content/renderer/renderer_blink_platform_impl.cc index e42a1a04..bfef67e2 100644 --- a/content/renderer/renderer_blink_platform_impl.cc +++ b/content/renderer/renderer_blink_platform_impl.cc
@@ -15,6 +15,9 @@ #include "base/strings/string_number_conversions.h" #include "base/strings/utf_string_conversions.h" #include "cc/blink/context_provider_web_context.h" +#include "components/scheduler/child/web_scheduler_impl.h" +#include "components/scheduler/renderer/renderer_scheduler.h" +#include "components/scheduler/renderer/webthread_impl_for_renderer_scheduler.h" #include "content/child/database_util.h" #include "content/child/file_info_util.h" #include "content/child/fileapi/webfilesystem_impl.h" @@ -22,7 +25,6 @@ #include "content/child/npapi/npobject_util.h" #include "content/child/quota_dispatcher.h" #include "content/child/quota_message_filter.h" -#include "content/child/scheduler/web_scheduler_impl.h" #include "content/child/simple_webmimeregistry_impl.h" #include "content/child/thread_safe_sender.h" #include "content/child/web_database_observer_impl.h" @@ -52,8 +54,6 @@ #include "content/renderer/media/renderer_webmidiaccessor_impl.h" #include "content/renderer/render_thread_impl.h" #include "content/renderer/renderer_clipboard_delegate.h" -#include "content/renderer/scheduler/renderer_scheduler.h" -#include "content/renderer/scheduler/webthread_impl_for_renderer_scheduler.h" #include "content/renderer/screen_orientation/screen_orientation_observer.h" #include "content/renderer/webclipboard_impl.h" #include "content/renderer/webgraphicscontext3d_provider_impl.h" @@ -221,9 +221,10 @@ //------------------------------------------------------------------------------ RendererBlinkPlatformImpl::RendererBlinkPlatformImpl( - RendererScheduler* renderer_scheduler) + scheduler::RendererScheduler* renderer_scheduler) : BlinkPlatformImpl(renderer_scheduler->DefaultTaskRunner()), - main_thread_(new WebThreadImplForRendererScheduler(renderer_scheduler)), + main_thread_( + new scheduler::WebThreadImplForRendererScheduler(renderer_scheduler)), clipboard_delegate_(new RendererClipboardDelegate), clipboard_(new WebClipboardImpl(clipboard_delegate_.get())), mime_registry_(new RendererBlinkPlatformImpl::MimeRegistry),
diff --git a/content/renderer/renderer_blink_platform_impl.h b/content/renderer/renderer_blink_platform_impl.h index da3ec90..d5fc8d360 100644 --- a/content/renderer/renderer_blink_platform_impl.h +++ b/content/renderer/renderer_blink_platform_impl.h
@@ -37,6 +37,11 @@ class WebServiceWorkerCacheStorage; } +namespace scheduler { +class RendererScheduler; +class WebThreadImplForRendererScheduler; +} + namespace content { class BatteryStatusDispatcher; class DeviceLightEventPump; @@ -45,17 +50,16 @@ class PlatformEventObserverBase; class QuotaMessageFilter; class RendererClipboardDelegate; -class RendererScheduler; class RenderView; class ThreadSafeSender; class WebClipboardImpl; class WebDatabaseObserverImpl; class WebFileSystemImpl; -class WebThreadImplForRendererScheduler; class CONTENT_EXPORT RendererBlinkPlatformImpl : public BlinkPlatformImpl { public: - explicit RendererBlinkPlatformImpl(RendererScheduler* renderer_scheduler); + explicit RendererBlinkPlatformImpl( + scheduler::RendererScheduler* renderer_scheduler); virtual ~RendererBlinkPlatformImpl(); void set_plugin_refresh_allowed(bool plugin_refresh_allowed) { @@ -208,7 +212,7 @@ void SendFakeDeviceEventDataForTesting(blink::WebPlatformEventType type); device::VibrationManagerPtr& GetConnectedVibrationManagerService(); - scoped_ptr<WebThreadImplForRendererScheduler> main_thread_; + scoped_ptr<scheduler::WebThreadImplForRendererScheduler> main_thread_; scoped_ptr<RendererClipboardDelegate> clipboard_delegate_; scoped_ptr<WebClipboardImpl> clipboard_;
diff --git a/content/renderer/scheduler/resource_dispatch_throttler.cc b/content/renderer/scheduler/resource_dispatch_throttler.cc index 927db73d..d2f7916 100644 --- a/content/renderer/scheduler/resource_dispatch_throttler.cc +++ b/content/renderer/scheduler/resource_dispatch_throttler.cc
@@ -6,8 +6,8 @@ #include "base/auto_reset.h" #include "base/trace_event/trace_event.h" +#include "components/scheduler/renderer/renderer_scheduler.h" #include "content/common/resource_messages.h" -#include "content/renderer/scheduler/renderer_scheduler.h" #include "ipc/ipc_message_macros.h" namespace content { @@ -21,7 +21,7 @@ ResourceDispatchThrottler::ResourceDispatchThrottler( IPC::Sender* proxied_sender, - RendererScheduler* scheduler, + scheduler::RendererScheduler* scheduler, base::TimeDelta flush_period, uint32 max_requests_per_flush) : proxied_sender_(proxied_sender),
diff --git a/content/renderer/scheduler/resource_dispatch_throttler.h b/content/renderer/scheduler/resource_dispatch_throttler.h index 233dea5a..66fc786 100644 --- a/content/renderer/scheduler/resource_dispatch_throttler.h +++ b/content/renderer/scheduler/resource_dispatch_throttler.h
@@ -13,9 +13,11 @@ #include "content/common/content_export.h" #include "ipc/ipc_sender.h" -namespace content { - +namespace scheduler { class RendererScheduler; +} + +namespace content { // Utility class for throttling a stream of resource requests targetted to a // specific IPC sender. The throttling itself is very basic: @@ -31,7 +33,7 @@ // |flush_period| and |max_requests_per_flush| must be strictly positive // in duration/value. ResourceDispatchThrottler(IPC::Sender* proxied_sender, - RendererScheduler* scheduler, + scheduler::RendererScheduler* scheduler, base::TimeDelta flush_period, uint32 max_requests_per_flush); ~ResourceDispatchThrottler() override; @@ -53,7 +55,7 @@ base::ThreadChecker thread_checker_; IPC::Sender* const proxied_sender_; - RendererScheduler* const scheduler_; + scheduler::RendererScheduler* const scheduler_; const base::TimeDelta flush_period_; const uint32 max_requests_per_flush_;
diff --git a/content/renderer/scheduler/resource_dispatch_throttler_unittest.cc b/content/renderer/scheduler/resource_dispatch_throttler_unittest.cc index 8217f7a..8b9dc14 100644 --- a/content/renderer/scheduler/resource_dispatch_throttler_unittest.cc +++ b/content/renderer/scheduler/resource_dispatch_throttler_unittest.cc
@@ -65,7 +65,7 @@ class ResourceDispatchThrottlerForTest : public ResourceDispatchThrottler { public: ResourceDispatchThrottlerForTest(IPC::Sender* sender, - RendererScheduler* scheduler) + scheduler::RendererScheduler* scheduler) : ResourceDispatchThrottler( sender, scheduler,
diff --git a/content/renderer/speech_recognition_dispatcher.cc b/content/renderer/speech_recognition_dispatcher.cc index dea16bc..cb78735 100644 --- a/content/renderer/speech_recognition_dispatcher.cc +++ b/content/renderer/speech_recognition_dispatcher.cc
@@ -164,21 +164,25 @@ case SPEECH_RECOGNITION_ERROR_NONE: NOTREACHED(); return WebSpeechRecognizerClient::OtherError; + case SPEECH_RECOGNITION_ERROR_NO_SPEECH: + return WebSpeechRecognizerClient::NoSpeechError; case SPEECH_RECOGNITION_ERROR_ABORTED: return WebSpeechRecognizerClient::AbortedError; - case SPEECH_RECOGNITION_ERROR_AUDIO: + case SPEECH_RECOGNITION_ERROR_AUDIO_CAPTURE: return WebSpeechRecognizerClient::AudioCaptureError; case SPEECH_RECOGNITION_ERROR_NETWORK: return WebSpeechRecognizerClient::NetworkError; case SPEECH_RECOGNITION_ERROR_NOT_ALLOWED: return WebSpeechRecognizerClient::NotAllowedError; - case SPEECH_RECOGNITION_ERROR_NO_SPEECH: - return WebSpeechRecognizerClient::NoSpeechError; + case SPEECH_RECOGNITION_ERROR_SERVICE_NOT_ALLOWED: + return WebSpeechRecognizerClient::ServiceNotAllowedError; + case SPEECH_RECOGNITION_ERROR_BAD_GRAMMAR: + return WebSpeechRecognizerClient::BadGrammarError; + case SPEECH_RECOGNITION_ERROR_LANGUAGE_NOT_SUPPORTED: + return WebSpeechRecognizerClient::LanguageNotSupportedError; case SPEECH_RECOGNITION_ERROR_NO_MATCH: NOTREACHED(); return WebSpeechRecognizerClient::OtherError; - case SPEECH_RECOGNITION_ERROR_BAD_GRAMMAR: - return WebSpeechRecognizerClient::BadGrammarError; } NOTREACHED(); return WebSpeechRecognizerClient::OtherError;
diff --git a/content/renderer/web_ui_runner.cc b/content/renderer/web_ui_runner.cc index 560342f..5d6f05c 100644 --- a/content/renderer/web_ui_runner.cc +++ b/content/renderer/web_ui_runner.cc
@@ -45,14 +45,8 @@ v8::Handle<v8::Value> receiver, int argc, v8::Handle<v8::Value> argv[]) { -#ifdef WEB_FRAME_USES_V8_LOCAL - v8::Local<v8::Value>* cast_argv = - reinterpret_cast<v8::Local<v8::Value>*>(argv); -#else - v8::Handle<v8::Value>* cast_argv = argv; -#endif return frame_->callFunctionEvenIfScriptDisabled(function, receiver, argc, - cast_argv); + argv); } gin::ContextHolder* WebUIRunner::GetContextHolder() {
diff --git a/content/shell/BUILD.gn b/content/shell/BUILD.gn index e1d9b116..08a5e56 100644 --- a/content/shell/BUILD.gn +++ b/content/shell/BUILD.gn
@@ -249,6 +249,7 @@ "//base/third_party/dynamic_annotations", "//cc", "//components/crash/app", + "//components/devtools_discovery", "//components/devtools_http_handler", "//components/web_cache/renderer", "//content:resources",
diff --git a/content/shell/DEPS b/content/shell/DEPS index 44638c3..552ba8e 100644 --- a/content/shell/DEPS +++ b/content/shell/DEPS
@@ -24,6 +24,7 @@ "+ui/views", "+components/crash", + "+components/devtools_discovery", "+components/devtools_http_handler", # For enabling media related features.
diff --git a/content/shell/android/BUILD.gn b/content/shell/android/BUILD.gn index 096ac679..3ad831e 100644 --- a/content/shell/android/BUILD.gn +++ b/content/shell/android/BUILD.gn
@@ -157,6 +157,7 @@ datadeps = [ ":content_shell_apk", ] + apk_under_test = ":content_shell_apk" apk_name = "ContentShellTest" android_manifest = "javatests/AndroidManifest.xml" }
diff --git a/content/shell/android/java/src/org/chromium/content_shell/Shell.java b/content/shell/android/java/src/org/chromium/content_shell/Shell.java index 6795c16..29d9ecd 100644 --- a/content/shell/android/java/src/org/chromium/content_shell/Shell.java +++ b/content/shell/android/java/src/org/chromium/content_shell/Shell.java
@@ -287,7 +287,7 @@ private void initFromNativeTabContents(WebContents webContents) { Context context = getContext(); mContentViewCore = new ContentViewCore(context); - ContentView cv = ContentView.newInstance(context, mContentViewCore); + ContentView cv = new ContentView(context, mContentViewCore); mContentViewCore.initialize(cv, cv, webContents, mWindow); mContentViewCore.setContentViewClient(mContentViewClient); mWebContents = mContentViewCore.getWebContents();
diff --git a/content/shell/android/javatests/src/org/chromium/content_shell_apk/ContentShellTestBase.java b/content/shell/android/javatests/src/org/chromium/content_shell_apk/ContentShellTestBase.java index 927496774..15d097d 100644 --- a/content/shell/android/javatests/src/org/chromium/content_shell_apk/ContentShellTestBase.java +++ b/content/shell/android/javatests/src/org/chromium/content_shell_apk/ContentShellTestBase.java
@@ -200,7 +200,7 @@ ThreadUtils.runOnUiThreadBlocking(new Runnable() { @Override public void run() { - ContentView cv = ContentView.newInstance(getActivity(), getContentViewCore()); + ContentView cv = new ContentView(getActivity(), getContentViewCore()); ((ViewGroup) getContentViewCore().getContainerView().getParent()).addView(cv); getContentViewCore().setContainerView(cv); getContentViewCore().setContainerViewInternals(cv);
diff --git a/content/shell/browser/layout_test/layout_test_browser_context.cc b/content/shell/browser/layout_test/layout_test_browser_context.cc index 259a407..be19bb2 100644 --- a/content/shell/browser/layout_test/layout_test_browser_context.cc +++ b/content/shell/browser/layout_test/layout_test_browser_context.cc
@@ -69,12 +69,6 @@ return push_messaging_service_.get(); } -LayoutTestPushMessagingService* -LayoutTestBrowserContext::GetLayoutTestPushMessagingService() { - return static_cast<LayoutTestPushMessagingService*>( - GetPushMessagingService()); -} - PermissionManager* LayoutTestBrowserContext::GetPermissionManager() { if (!permission_manager_.get()) permission_manager_.reset(new LayoutTestPermissionManager());
diff --git a/content/shell/browser/layout_test/layout_test_browser_context.h b/content/shell/browser/layout_test/layout_test_browser_context.h index d9c9832f..93fd10e 100644 --- a/content/shell/browser/layout_test/layout_test_browser_context.h +++ b/content/shell/browser/layout_test/layout_test_browser_context.h
@@ -30,7 +30,6 @@ PushMessagingService* GetPushMessagingService() override; PermissionManager* GetPermissionManager() override; - LayoutTestPushMessagingService* GetLayoutTestPushMessagingService(); LayoutTestPermissionManager* GetLayoutTestPermissionManager(); protected:
diff --git a/content/shell/browser/layout_test/layout_test_message_filter.cc b/content/shell/browser/layout_test/layout_test_message_filter.cc index fcae408c..6b9eaf6 100644 --- a/content/shell/browser/layout_test/layout_test_message_filter.cc +++ b/content/shell/browser/layout_test/layout_test_message_filter.cc
@@ -12,7 +12,6 @@ #include "content/shell/browser/layout_test/layout_test_content_browser_client.h" #include "content/shell/browser/layout_test/layout_test_notification_manager.h" #include "content/shell/browser/layout_test/layout_test_permission_manager.h" -#include "content/shell/browser/layout_test/layout_test_push_messaging_service.h" #include "content/shell/browser/shell_browser_context.h" #include "content/shell/browser/shell_content_browser_client.h" #include "content/shell/browser/shell_network_delegate.h" @@ -47,8 +46,6 @@ if (message.type() == LayoutTestHostMsg_ClearAllDatabases::ID) *thread = BrowserThread::FILE; if (message.type() == LayoutTestHostMsg_SimulateWebNotificationClick::ID || - message.type() == LayoutTestHostMsg_SetPushMessagingPermission::ID || - message.type() == LayoutTestHostMsg_ClearPushMessagingPermissions::ID || message.type() == LayoutTestHostMsg_SetPermission::ID || message.type() == LayoutTestHostMsg_ResetPermissions::ID) *thread = BrowserThread::UI; @@ -69,10 +66,6 @@ OnClearWebNotificationPermissions) IPC_MESSAGE_HANDLER(LayoutTestHostMsg_SimulateWebNotificationClick, OnSimulateWebNotificationClick) - IPC_MESSAGE_HANDLER(LayoutTestHostMsg_SetPushMessagingPermission, - OnSetPushMessagingPermission) - IPC_MESSAGE_HANDLER(LayoutTestHostMsg_ClearPushMessagingPermissions, - OnClearPushMessagingPermissions) IPC_MESSAGE_HANDLER(LayoutTestHostMsg_AcceptAllCookies, OnAcceptAllCookies) IPC_MESSAGE_HANDLER(LayoutTestHostMsg_DeleteAllCookies, OnDeleteAllCookies) IPC_MESSAGE_HANDLER(LayoutTestHostMsg_SetPermission, OnSetPermission) @@ -143,23 +136,6 @@ manager->SimulateClick(title); } -void LayoutTestMessageFilter::OnSetPushMessagingPermission(const GURL& origin, - bool allowed) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - LayoutTestContentBrowserClient::Get() - ->GetLayoutTestBrowserContext() - ->GetLayoutTestPushMessagingService() - ->SetPermission(origin, allowed); -} - -void LayoutTestMessageFilter::OnClearPushMessagingPermissions() { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - LayoutTestContentBrowserClient::Get() - ->GetLayoutTestBrowserContext() - ->GetLayoutTestPushMessagingService() - ->ClearPermissions(); -} - void LayoutTestMessageFilter::OnAcceptAllCookies(bool accept) { ShellNetworkDelegate::SetAcceptAllCookies(accept); }
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 53159fbe..36c96c8 100644 --- a/content/shell/browser/layout_test/layout_test_permission_manager.cc +++ b/content/shell/browser/layout_test/layout_test_permission_manager.cc
@@ -88,9 +88,9 @@ return; } - callback.Run(GetPermissionStatus(permission, - requesting_origin, - web_contents->GetLastCommittedURL())); + callback.Run(GetPermissionStatus( + permission, requesting_origin, + web_contents->GetLastCommittedURL().GetOrigin())); } void LayoutTestPermissionManager::CancelPermissionRequest(
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 85c0932..b3a255b2 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
@@ -6,25 +6,38 @@ #include "base/callback.h" #include "base/logging.h" +#include "content/public/browser/permission_type.h" +#include "content/shell/browser/layout_test/layout_test_browser_context.h" +#include "content/shell/browser/layout_test/layout_test_content_browser_client.h" +#include "content/shell/browser/layout_test/layout_test_permission_manager.h" namespace content { +namespace { + +blink::WebPushPermissionStatus ToWebPushPermissionStatus( + PermissionStatus status) { + switch (status) { + case PERMISSION_STATUS_GRANTED: + return blink::WebPushPermissionStatusGranted; + case PERMISSION_STATUS_DENIED: + return blink::WebPushPermissionStatusDenied; + case PERMISSION_STATUS_ASK: + return blink::WebPushPermissionStatusDefault; + } + + NOTREACHED(); + return blink::WebPushPermissionStatusLast; +} + +} // anonymous namespace + LayoutTestPushMessagingService::LayoutTestPushMessagingService() { } LayoutTestPushMessagingService::~LayoutTestPushMessagingService() { } -void LayoutTestPushMessagingService::SetPermission(const GURL& origin, - bool allowed) { - permission_map_[origin] = allowed ? blink::WebPushPermissionStatusGranted - : blink::WebPushPermissionStatusDenied; -} - -void LayoutTestPushMessagingService::ClearPermissions() { - permission_map_.clear(); -} - GURL LayoutTestPushMessagingService::GetPushEndpoint() { return GURL("https://example.com/LayoutTestEndpoint"); } @@ -61,10 +74,12 @@ const GURL& requesting_origin, const GURL& embedding_origin, bool user_visible) { - const auto& it = permission_map_.find(requesting_origin); - if (it == permission_map_.end()) - return blink::WebPushPermissionStatusDefault; - return it->second; + return ToWebPushPermissionStatus(LayoutTestContentBrowserClient::Get() + ->GetLayoutTestBrowserContext() + ->GetLayoutTestPermissionManager() + ->GetPermissionStatus(PermissionType::PUSH_MESSAGING, + requesting_origin, + embedding_origin)); } void LayoutTestPushMessagingService::Unregister(
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 2fac0b0..1158963 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
@@ -19,9 +19,6 @@ LayoutTestPushMessagingService(); ~LayoutTestPushMessagingService() override; - void SetPermission(const GURL& origin, bool allowed); - void ClearPermissions(); - // PushMessagingService implementation: GURL GetPushEndpoint() override; void RegisterFromDocument( @@ -48,9 +45,6 @@ const UnregisterCallback& callback) override; private: - // Map from origin to permission status. - std::map<GURL, blink::WebPushPermissionStatus> permission_map_; - DISALLOW_COPY_AND_ASSIGN(LayoutTestPushMessagingService); };
diff --git a/content/shell/browser/shell_devtools_manager_delegate.cc b/content/shell/browser/shell_devtools_manager_delegate.cc index 8011145..5384e39 100644 --- a/content/shell/browser/shell_devtools_manager_delegate.cc +++ b/content/shell/browser/shell_devtools_manager_delegate.cc
@@ -12,6 +12,8 @@ #include "base/strings/string_number_conversions.h" #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" +#include "components/devtools_discovery/basic_target_descriptor.h" +#include "components/devtools_discovery/devtools_discovery_manager.h" #include "components/devtools_http_handler/devtools_http_handler.h" #include "content/public/browser/devtools_agent_host.h" #include "content/public/browser/devtools_frontend_host.h" @@ -45,9 +47,6 @@ const char kFrontEndURL[] = "http://chrome-devtools-frontend.appspot.com/serve_rev/%s/inspector.html"; #endif -const char kTargetTypePage[] = "page"; -const char kTargetTypeServiceWorker[] = "service_worker"; -const char kTargetTypeOther[] = "other"; const int kBackLog = 10; @@ -133,62 +132,6 @@ #endif } -class Target : public DevToolsTarget { - public: - explicit Target(scoped_refptr<DevToolsAgentHost> agent_host); - - std::string GetId() const override { return agent_host_->GetId(); } - std::string GetParentId() const override { return std::string(); } - std::string GetType() const override { - switch (agent_host_->GetType()) { - case DevToolsAgentHost::TYPE_WEB_CONTENTS: - return kTargetTypePage; - case DevToolsAgentHost::TYPE_SERVICE_WORKER: - return kTargetTypeServiceWorker; - default: - break; - } - return kTargetTypeOther; - } - std::string GetTitle() const override { return agent_host_->GetTitle(); } - std::string GetDescription() const override { return std::string(); } - GURL GetURL() const override { return agent_host_->GetURL(); } - GURL GetFaviconURL() const override { return favicon_url_; } - base::TimeTicks GetLastActivityTime() const override { - return last_activity_time_; - } - bool IsAttached() const override { return agent_host_->IsAttached(); } - scoped_refptr<DevToolsAgentHost> GetAgentHost() const override { - return agent_host_; - } - bool Activate() const override; - bool Close() const override; - - private: - scoped_refptr<DevToolsAgentHost> agent_host_; - GURL favicon_url_; - base::TimeTicks last_activity_time_; -}; - -Target::Target(scoped_refptr<DevToolsAgentHost> agent_host) - : agent_host_(agent_host) { - if (WebContents* web_contents = agent_host_->GetWebContents()) { - NavigationController& controller = web_contents->GetController(); - NavigationEntry* entry = controller.GetActiveEntry(); - if (entry != NULL && entry->GetURL().is_valid()) - favicon_url_ = entry->GetFavicon().url; - last_activity_time_ = web_contents->GetLastActiveTime(); - } -} - -bool Target::Activate() const { - return agent_host_->Activate(); -} - -bool Target::Close() const { - return agent_host_->Close(); -} - // ShellDevToolsDelegate ---------------------------------------------------- class ShellDevToolsDelegate : @@ -277,14 +220,16 @@ NULL, gfx::Size()); return scoped_ptr<DevToolsTarget>( - new Target(DevToolsAgentHost::GetOrCreateFor(shell->web_contents()))); + new devtools_discovery::BasicTargetDescriptor( + DevToolsAgentHost::GetOrCreateFor(shell->web_contents()))); } void ShellDevToolsManagerDelegate::EnumerateTargets(TargetCallback callback) { TargetList targets; - for (const auto& agent_host : DevToolsAgentHost::GetOrCreateAll()) { - targets.push_back(new Target(agent_host)); - } + devtools_discovery::DevToolsDiscoveryManager* discovery_manager = + devtools_discovery::DevToolsDiscoveryManager::GetInstance(); + for (const auto& descriptor : discovery_manager->GetDescriptors()) + targets.push_back(descriptor); callback.Run(targets); }
diff --git a/content/shell/common/layout_test/layout_test_messages.h b/content/shell/common/layout_test/layout_test_messages.h index ed3a56b..15160744 100644 --- a/content/shell/common/layout_test/layout_test_messages.h +++ b/content/shell/common/layout_test/layout_test_messages.h
@@ -33,10 +33,6 @@ IPC_MESSAGE_ROUTED0(LayoutTestHostMsg_ClearWebNotificationPermissions) IPC_MESSAGE_ROUTED1(LayoutTestHostMsg_SimulateWebNotificationClick, std::string /* title */) -IPC_MESSAGE_ROUTED2(LayoutTestHostMsg_SetPushMessagingPermission, - GURL /* origin */, - bool /* allowed */) -IPC_MESSAGE_ROUTED0(LayoutTestHostMsg_ClearPushMessagingPermissions) IPC_MESSAGE_ROUTED1(LayoutTestHostMsg_AcceptAllCookies, bool /* accept */) IPC_MESSAGE_ROUTED0(LayoutTestHostMsg_DeleteAllCookies)
diff --git a/content/shell/renderer/layout_test/webkit_test_runner.cc b/content/shell/renderer/layout_test/webkit_test_runner.cc index e025926a..97222a9 100644 --- a/content/shell/renderer/layout_test/webkit_test_runner.cc +++ b/content/shell/renderer/layout_test/webkit_test_runner.cc
@@ -439,16 +439,6 @@ Send(new LayoutTestHostMsg_SimulateWebNotificationClick(routing_id(), title)); } -void WebKitTestRunner::SetPushMessagingPermission(const GURL& origin, - bool allowed) { - Send(new LayoutTestHostMsg_SetPushMessagingPermission(routing_id(), origin, - allowed)); -} - -void WebKitTestRunner::ClearPushMessagingPermissions() { - Send(new LayoutTestHostMsg_ClearPushMessagingPermissions(routing_id())); -} - void WebKitTestRunner::SetDeviceScaleFactor(float factor) { content::SetDeviceScaleFactor(render_view(), factor); }
diff --git a/content/shell/renderer/layout_test/webkit_test_runner.h b/content/shell/renderer/layout_test/webkit_test_runner.h index e63ed68..a8271459 100644 --- a/content/shell/renderer/layout_test/webkit_test_runner.h +++ b/content/shell/renderer/layout_test/webkit_test_runner.h
@@ -92,8 +92,6 @@ bool permission_granted) override; void ClearWebNotificationPermissions() override; void SimulateWebNotificationClick(const std::string& title) override; - void SetPushMessagingPermission(const GURL& origin, bool allowed) override; - void ClearPushMessagingPermissions() override; void SetDeviceScaleFactor(float factor) override; void SetDeviceColorProfile(const std::string& name) override; void SetBluetoothMockDataSet(const std::string& name) override;
diff --git a/content/shell/renderer/test_runner/accessibility_controller.cc b/content/shell/renderer/test_runner/accessibility_controller.cc index 4aafbed4..678f56d 100644 --- a/content/shell/renderer/test_runner/accessibility_controller.cc +++ b/content/shell/renderer/test_runner/accessibility_controller.cc
@@ -191,11 +191,7 @@ return; // Call global notification listeners. -#ifdef WEB_FRAME_USES_V8_LOCAL - v8::Local<v8::Value> argv[] = { -#else v8::Handle<v8::Value> argv[] = { -#endif element_handle, v8::String::NewFromUtf8(isolate, notification_name.data(), v8::String::kNormalString,
diff --git a/content/shell/renderer/test_runner/test_runner.cc b/content/shell/renderer/test_runner/test_runner.cc index 7177151..3b017d9 100644 --- a/content/shell/renderer/test_runner/test_runner.cc +++ b/content/shell/renderer/test_runner/test_runner.cc
@@ -41,7 +41,6 @@ #include "third_party/WebKit/public/web/WebInputElement.h" #include "third_party/WebKit/public/web/WebKit.h" #include "third_party/WebKit/public/web/WebLocalFrame.h" -#include "third_party/WebKit/public/web/WebMIDIClientMock.h" #include "third_party/WebKit/public/web/WebPageOverlay.h" #include "third_party/WebKit/public/web/WebScriptSource.h" #include "third_party/WebKit/public/web/WebSecurityPolicy.h" @@ -105,15 +104,9 @@ v8::Context::Scope context_scope(context); -#ifdef WEB_FRAME_USES_V8_LOCAL - scoped_ptr<v8::Local<v8::Value>[]> local_argv; - if (argc_) { - local_argv.reset(new v8::Local<v8::Value>[argc_]); -#else scoped_ptr<v8::Handle<v8::Value>[]> local_argv; if (argc_) { local_argv.reset(new v8::Handle<v8::Value>[argc_]); -#endif for (int i = 0; i < argc_; ++i) local_argv[i] = v8::Local<v8::Value>::New(isolate, argv_[i]); } @@ -307,8 +300,6 @@ v8::Handle<v8::Function> callback); void SetCustomTextOutput(std::string output); void SetViewSourceForFrame(const std::string& name, bool enabled); - void SetPushMessagingPermission(const std::string& origin, bool allowed); - void ClearPushMessagingPermissions(); void SetBluetoothMockDataSet(const std::string& dataset_name); void SetGeofencingMockProvider(bool service_available); void ClearGeofencingMockProvider(); @@ -562,10 +553,6 @@ &TestRunnerBindings::SetCustomTextOutput) .SetMethod("setViewSourceForFrame", &TestRunnerBindings::SetViewSourceForFrame) - .SetMethod("setPushMessagingPermission", - &TestRunnerBindings::SetPushMessagingPermission) - .SetMethod("clearPushMessagingPermissions", - &TestRunnerBindings::ClearPushMessagingPermissions) .SetMethod("setBluetoothMockDataSet", &TestRunnerBindings::SetBluetoothMockDataSet) .SetMethod("forceNextWebGLContextCreationToFail", @@ -1337,8 +1324,6 @@ } void TestRunnerBindings::SetMIDISysexPermission(bool value) { - if (runner_) - runner_->SetMIDISysexPermission(value); } void TestRunnerBindings::GrantWebNotificationPermission(gin::Arguments* args) { @@ -1441,17 +1426,6 @@ } } -void TestRunnerBindings::SetPushMessagingPermission(const std::string& origin, - bool allowed) { - if (runner_) - runner_->SetPushMessagingPermission(GURL(origin), allowed); -} - -void TestRunnerBindings::ClearPushMessagingPermissions() { - if (runner_) - runner_->ClearPushMessagingPermissions(); -} - void TestRunnerBindings::SetGeofencingMockProvider(bool service_available) { if (runner_) runner_->SetGeofencingMockProvider(service_available); @@ -2855,11 +2829,6 @@ midi_accessor_result_ = result; } -void TestRunner::SetMIDISysexPermission(bool value) { - for (auto* window : test_interfaces_->GetWindowList()) - window->GetMIDIClientMock()->setSysexPermission(value); -} - void TestRunner::GrantWebNotificationPermission(const GURL& origin, bool permission_granted) { delegate_->GrantWebNotificationPermission(origin, permission_granted); @@ -3010,14 +2979,6 @@ InvokeCallback(task.Pass()); } -void TestRunner::SetPushMessagingPermission(const GURL& origin, bool allowed) { - delegate_->SetPushMessagingPermission(origin, allowed); -} - -void TestRunner::ClearPushMessagingPermissions() { - delegate_->ClearPushMessagingPermissions(); -} - void TestRunner::LocationChangeDone() { web_history_item_count_ = delegate_->NavigationEntryCount();
diff --git a/content/shell/renderer/test_runner/test_runner.h b/content/shell/renderer/test_runner/test_runner.h index 94d83ec..c55ab4b 100644 --- a/content/shell/renderer/test_runner/test_runner.h +++ b/content/shell/renderer/test_runner/test_runner.h
@@ -547,7 +547,6 @@ // MIDI function to control permission handling. void SetMIDIAccessorResult(bool result); - void SetMIDISysexPermission(bool value); // Grants permission for desktop notifications to an origin void GrantWebNotificationPermission(const GURL& origin, @@ -592,12 +591,6 @@ void CopyImageAtAndCapturePixelsAsyncThen( int x, int y, const v8::Handle<v8::Function> callback); - // Sets the origin's permission to use the Push API to granted or denied. - void SetPushMessagingPermission(const GURL& origin, bool allowed); - - // Clears all previously granted Push API permissions. - void ClearPushMessagingPermissions(); - void GetManifestThen(v8::Handle<v8::Function> callback); ///////////////////////////////////////////////////////////////////////////
diff --git a/content/shell/renderer/test_runner/web_ax_object_proxy.cc b/content/shell/renderer/test_runner/web_ax_object_proxy.cc index b0d6d66..cdff16e 100644 --- a/content/shell/renderer/test_runner/web_ax_object_proxy.cc +++ b/content/shell/renderer/test_runner/web_ax_object_proxy.cc
@@ -492,9 +492,6 @@ &WebAXObjectProxy::SelectionStartLineNumber) .SetProperty("selectionEndLineNumber", &WebAXObjectProxy::SelectionEndLineNumber) - .SetProperty("insertionPointLineNumber", - &WebAXObjectProxy::InsertionPointLineNumber) - .SetProperty("selectedTextRange", &WebAXObjectProxy::SelectedTextRange) .SetProperty("isEnabled", &WebAXObjectProxy::IsEnabled) .SetProperty("isRequired", &WebAXObjectProxy::IsRequired) .SetProperty("isFocused", &WebAXObjectProxy::IsFocused) @@ -513,6 +510,8 @@ .SetProperty("isValid", &WebAXObjectProxy::IsValid) .SetProperty("isReadOnly", &WebAXObjectProxy::IsReadOnly) .SetProperty("orientation", &WebAXObjectProxy::Orientation) + .SetProperty("posInSet", &WebAXObjectProxy::PosInSet) + .SetProperty("setSize", &WebAXObjectProxy::SetSize) .SetProperty("clickPointX", &WebAXObjectProxy::ClickPointX) .SetProperty("clickPointY", &WebAXObjectProxy::ClickPointY) .SetProperty("rowCount", &WebAXObjectProxy::RowCount) @@ -622,11 +621,7 @@ v8::Isolate* isolate = blink::mainThreadIsolate(); -#ifdef WEB_FRAME_USES_V8_LOCAL - v8::Local<v8::Value> argv[] = { -#else v8::Handle<v8::Value> argv[] = { -#endif v8::String::NewFromUtf8(isolate, notification_name.data(), v8::String::kNormalString, notification_name.size()), @@ -745,23 +740,6 @@ return accessibility_object_.selectionEndLineNumber(); } -// TODO(nektar): Remove this function after updating tests. -int WebAXObjectProxy::InsertionPointLineNumber() { - accessibility_object_.updateLayoutAndCheckValidity(); - if (!accessibility_object_.isFocused()) - return -1; - return accessibility_object_.selectionEndLineNumber(); -} - -// TODO(nektar): Remove this function after updating tests. -std::string WebAXObjectProxy::SelectedTextRange() { - accessibility_object_.updateLayoutAndCheckValidity(); - unsigned selection_start = accessibility_object_.selectionStart(); - unsigned selection_end = accessibility_object_.selectionEnd(); - return base::StringPrintf("{%d, %d}", - selection_start, selection_end - selection_start); -} - bool WebAXObjectProxy::IsEnabled() { accessibility_object_.updateLayoutAndCheckValidity(); return accessibility_object_.isEnabled(); @@ -853,6 +831,16 @@ return std::string(); } +int WebAXObjectProxy::PosInSet() { + accessibility_object_.updateLayoutAndCheckValidity(); + return accessibility_object_.posInSet(); +} + +int WebAXObjectProxy::SetSize() { + accessibility_object_.updateLayoutAndCheckValidity(); + return accessibility_object_.setSize(); +} + int WebAXObjectProxy::ClickPointX() { accessibility_object_.updateLayoutAndCheckValidity(); return accessibility_object_.clickPoint().x;
diff --git a/content/shell/renderer/test_runner/web_ax_object_proxy.h b/content/shell/renderer/test_runner/web_ax_object_proxy.h index 0c1eea0..7e86e620 100644 --- a/content/shell/renderer/test_runner/web_ax_object_proxy.h +++ b/content/shell/renderer/test_runner/web_ax_object_proxy.h
@@ -73,10 +73,6 @@ int SelectionEnd(); int SelectionStartLineNumber(); int SelectionEndLineNumber(); - // TODO(nektar): Remove this function after updating tests. - int InsertionPointLineNumber(); - // TODO(nektar): Remove this function after updating tests. - std::string SelectedTextRange(); bool IsEnabled(); bool IsRequired(); bool IsFocused(); @@ -94,6 +90,8 @@ bool IsValid(); bool IsReadOnly(); std::string Orientation(); + int PosInSet(); + int SetSize(); int ClickPointX(); int ClickPointY(); int32_t RowCount();
diff --git a/content/shell/renderer/test_runner/web_frame_test_proxy.h b/content/shell/renderer/test_runner/web_frame_test_proxy.h index c0541eb..8d860ff 100644 --- a/content/shell/renderer/test_runner/web_frame_test_proxy.h +++ b/content/shell/renderer/test_runner/web_frame_test_proxy.h
@@ -266,10 +266,6 @@ return base_proxy_->GetUserMediaClient(); } - virtual blink::WebMIDIClient* webMIDIClient() { - return base_proxy_->GetWebMIDIClient(); - } - virtual bool willCheckAndDispatchMessageEvent( blink::WebLocalFrame* source_frame, blink::WebFrame* target_frame,
diff --git a/content/shell/renderer/test_runner/web_test_delegate.h b/content/shell/renderer/test_runner/web_test_delegate.h index 304f8c9..ee03798 100644 --- a/content/shell/renderer/test_runner/web_test_delegate.h +++ b/content/shell/renderer/test_runner/web_test_delegate.h
@@ -140,10 +140,6 @@ virtual void ClearWebNotificationPermissions() = 0; virtual void SimulateWebNotificationClick(const std::string& title) = 0; - // Controls the Push API. - virtual void SetPushMessagingPermission(const GURL& origin, bool allowed) = 0; - virtual void ClearPushMessagingPermissions() = 0; - // Controls the device scale factor of the main WebView for hidpi tests. virtual void SetDeviceScaleFactor(float factor) = 0;
diff --git a/content/shell/renderer/test_runner/web_test_proxy.cc b/content/shell/renderer/test_runner/web_test_proxy.cc index d602bbc..ae522d3 100644 --- a/content/shell/renderer/test_runner/web_test_proxy.cc +++ b/content/shell/renderer/test_runner/web_test_proxy.cc
@@ -46,7 +46,6 @@ #include "third_party/WebKit/public/web/WebElement.h" #include "third_party/WebKit/public/web/WebHistoryItem.h" #include "third_party/WebKit/public/web/WebLocalFrame.h" -#include "third_party/WebKit/public/web/WebMIDIClientMock.h" #include "third_party/WebKit/public/web/WebNode.h" #include "third_party/WebKit/public/web/WebPagePopup.h" #include "third_party/WebKit/public/web/WebPluginParams.h" @@ -407,8 +406,6 @@ animate_scheduled_ = false; resource_identifier_map_.clear(); log_console_output_ = true; - if (midi_client_.get()) - midi_client_->resetMock(); accept_languages_ = ""; } @@ -678,12 +675,6 @@ return screen_orientation_client_.get(); } -blink::WebMIDIClientMock* WebTestProxyBase::GetMIDIClientMock() { - if (!midi_client_.get()) - midi_client_.reset(new blink::WebMIDIClientMock); - return midi_client_.get(); -} - MockWebSpeechRecognizer* WebTestProxyBase::GetSpeechRecognizerMock() { if (!speech_recognizer_.get()) { speech_recognizer_.reset(new MockWebSpeechRecognizer()); @@ -938,10 +929,6 @@ frame->printEnd(); } -blink::WebMIDIClient* WebTestProxyBase::GetWebMIDIClient() { - return GetMIDIClientMock(); -} - blink::WebSpeechRecognizer* WebTestProxyBase::GetSpeechRecognizer() { return GetSpeechRecognizerMock(); }
diff --git a/content/shell/renderer/test_runner/web_test_proxy.h b/content/shell/renderer/test_runner/web_test_proxy.h index d170c95..944b67c2 100644 --- a/content/shell/renderer/test_runner/web_test_proxy.h +++ b/content/shell/renderer/test_runner/web_test_proxy.h
@@ -48,8 +48,6 @@ class WebLocalFrame; class WebMIDIAccessor; class WebMIDIAccessorClient; -class WebMIDIClient; -class WebMIDIClientMock; class WebNode; class WebPlugin; class WebRange; @@ -128,7 +126,6 @@ void GetScreenOrientationForTesting(blink::WebScreenInfo&); MockScreenOrientationClient* GetScreenOrientationClientMock(); - blink::WebMIDIClientMock* GetMIDIClientMock(); MockWebSpeechRecognizer* GetSpeechRecognizerMock(); MockCredentialManagerClient* GetCredentialManagerClientMock(); @@ -168,7 +165,6 @@ const blink::WebContextMenuData& data); blink::WebUserMediaClient* GetUserMediaClient(); void PrintPage(blink::WebLocalFrame* frame); - blink::WebMIDIClient* GetWebMIDIClient(); blink::WebSpeechRecognizer* GetSpeechRecognizer(); bool RequestPointerLock(); void RequestPointerUnlock(); @@ -268,7 +264,6 @@ int chooser_count_; scoped_ptr<MockCredentialManagerClient> credential_manager_client_; - scoped_ptr<blink::WebMIDIClientMock> midi_client_; scoped_ptr<MockWebSpeechRecognizer> speech_recognizer_; scoped_ptr<MockScreenOrientationClient> screen_orientation_client_;
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn index fddbb5c..81823f0 100644 --- a/content/test/BUILD.gn +++ b/content/test/BUILD.gn
@@ -54,6 +54,7 @@ public_deps += [ "//third_party/WebKit/public:blink" ] deps += [ + "//components/scheduler:scheduler", "//content/browser/speech/proto", "//content/public/child:child_sources", "//content/gpu",
diff --git a/content/test/DEPS b/content/test/DEPS index b0b89c0..12876fd 100644 --- a/content/test/DEPS +++ b/content/test/DEPS
@@ -1,4 +1,8 @@ include_rules = [ + # Allow inclusion of specific components that we depend on. We may only + # depend on components which we share with the mojo html_viewer. + "+components/scheduler/renderer", + "+cc/blink", "+chromeos/audio", # For WebRTC tests. # Testing utilities can access anything in content/
diff --git a/content/test/data/accessibility/aria/aria-posinset-expected-android.txt b/content/test/data/accessibility/aria/aria-posinset-expected-android.txt new file mode 100644 index 0000000..4d08881 --- /dev/null +++ b/content/test/data/accessibility/aria/aria-posinset-expected-android.txt
@@ -0,0 +1,12 @@ +android.webkit.WebView focusable focused scrollable +++android.widget.ListView collection item_count=4 row_count=4 +++++android.view.View clickable collection_item focusable name='Item 1' +++++android.view.View clickable collection_item focusable name='Item 2' item_index=1 row_index=1 +++++android.view.View clickable collection_item focusable name='Item 3' item_index=2 row_index=2 +++++android.view.View clickable collection_item focusable name='Item 4' item_index=3 row_index=3 +++android.widget.ListView collection item_count=5 row_count=5 +++++android.view.View clickable collection_item focusable name='Item 1' +++++android.view.View clickable collection_item focusable name='Item 2' item_index=1 row_index=1 +++++android.view.View clickable collection_item focusable name='Item 3' item_index=2 row_index=2 +++++android.view.View clickable collection_item focusable name='Item 4' item_index=3 row_index=3 +++++android.view.View clickable collection_item focusable name='Item 5' item_index=4 row_index=4
diff --git a/content/test/data/accessibility/aria/aria-posinset-expected-mac.txt b/content/test/data/accessibility/aria/aria-posinset-expected-mac.txt new file mode 100644 index 0000000..36811b7e --- /dev/null +++ b/content/test/data/accessibility/aria/aria-posinset-expected-mac.txt
@@ -0,0 +1,12 @@ +AXWebArea AXRoleDescription='HTML content' +++AXList AXRoleDescription='list' +++++AXStaticText AXRoleDescription='text' AXTitle='Item 1' AXARIASetSize='4' AXARIAPosInSet='1' +++++AXStaticText AXRoleDescription='text' AXTitle='Item 2' AXARIASetSize='4' AXARIAPosInSet='2' +++++AXStaticText AXRoleDescription='text' AXTitle='Item 3' AXARIASetSize='4' AXARIAPosInSet='3' +++++AXStaticText AXRoleDescription='text' AXTitle='Item 4' AXARIASetSize='4' AXARIAPosInSet='4' +++AXList AXRoleDescription='list' +++++AXStaticText AXRoleDescription='text' AXTitle='Item 1' AXARIASetSize='5' AXARIAPosInSet='1' +++++AXStaticText AXRoleDescription='text' AXTitle='Item 2' AXARIASetSize='5' AXARIAPosInSet='2' +++++AXStaticText AXRoleDescription='text' AXTitle='Item 3' AXARIASetSize='5' AXARIAPosInSet='3' +++++AXStaticText AXRoleDescription='text' AXTitle='Item 4' AXARIASetSize='5' AXARIAPosInSet='4' +++++AXStaticText AXRoleDescription='text' AXTitle='Item 5' AXARIASetSize='5' AXARIAPosInSet='5'
diff --git a/content/test/data/accessibility/aria/aria-posinset-expected-win.txt b/content/test/data/accessibility/aria/aria-posinset-expected-win.txt new file mode 100644 index 0000000..6cae42b --- /dev/null +++ b/content/test/data/accessibility/aria/aria-posinset-expected-win.txt
@@ -0,0 +1,12 @@ +ROLE_SYSTEM_DOCUMENT READONLY FOCUSABLE +++ROLE_SYSTEM_LIST +++++ROLE_SYSTEM_LISTITEM name='Item 1' FOCUSABLE setsize:4 posinset:1 +++++ROLE_SYSTEM_LISTITEM name='Item 2' FOCUSABLE setsize:4 posinset:2 +++++ROLE_SYSTEM_LISTITEM name='Item 3' FOCUSABLE setsize:4 posinset:3 +++++ROLE_SYSTEM_LISTITEM name='Item 4' FOCUSABLE setsize:4 posinset:4 +++ROLE_SYSTEM_LIST +++++ROLE_SYSTEM_LISTITEM name='Item 1' FOCUSABLE setsize:5 posinset:1 +++++ROLE_SYSTEM_LISTITEM name='Item 2' FOCUSABLE setsize:5 posinset:2 +++++ROLE_SYSTEM_LISTITEM name='Item 3' FOCUSABLE setsize:5 posinset:3 +++++ROLE_SYSTEM_LISTITEM name='Item 4' FOCUSABLE setsize:5 posinset:4 +++++ROLE_SYSTEM_LISTITEM name='Item 5' FOCUSABLE setsize:5 posinset:5
diff --git a/content/test/data/accessibility/aria/aria-posinset.html b/content/test/data/accessibility/aria/aria-posinset.html new file mode 100644 index 0000000..e9ffd1c --- /dev/null +++ b/content/test/data/accessibility/aria/aria-posinset.html
@@ -0,0 +1,23 @@ +<!-- +@MAC-ALLOW:AXRole* +@MAC-ALLOW:AXARIA* +@WIN-ALLOW:setsize* +@WIN-ALLOW:posinset* +--> +<html> +<body> +<div role="listbox"> + <div tabIndex="0" aria-setsize="4" aria-posinset="1" role="option">Item 1</div> + <div tabIndex="0" aria-setsize="4" aria-posinset="2" role="option">Item 2</div> + <div tabIndex="0" aria-setsize="4" aria-posinset="3" role="option">Item 3</div> + <div tabIndex="0" aria-setsize="4" aria-posinset="4" role="option">Item 4</div> +</div> +<div role="listbox"> + <div tabIndex="0" role="option">Item 1</div> + <div tabIndex="0" role="option">Item 2</div> + <div tabIndex="0" role="option">Item 3</div> + <div tabIndex="0" role="option">Item 4</div> + <div tabIndex="0" role="option">Item 5</div> +</div> +</body> +</html>
diff --git a/content/test/data/accessibility/aria/aria-setsize-expected-android.txt b/content/test/data/accessibility/aria/aria-setsize-expected-android.txt new file mode 100644 index 0000000..4d08881 --- /dev/null +++ b/content/test/data/accessibility/aria/aria-setsize-expected-android.txt
@@ -0,0 +1,12 @@ +android.webkit.WebView focusable focused scrollable +++android.widget.ListView collection item_count=4 row_count=4 +++++android.view.View clickable collection_item focusable name='Item 1' +++++android.view.View clickable collection_item focusable name='Item 2' item_index=1 row_index=1 +++++android.view.View clickable collection_item focusable name='Item 3' item_index=2 row_index=2 +++++android.view.View clickable collection_item focusable name='Item 4' item_index=3 row_index=3 +++android.widget.ListView collection item_count=5 row_count=5 +++++android.view.View clickable collection_item focusable name='Item 1' +++++android.view.View clickable collection_item focusable name='Item 2' item_index=1 row_index=1 +++++android.view.View clickable collection_item focusable name='Item 3' item_index=2 row_index=2 +++++android.view.View clickable collection_item focusable name='Item 4' item_index=3 row_index=3 +++++android.view.View clickable collection_item focusable name='Item 5' item_index=4 row_index=4
diff --git a/content/test/data/accessibility/aria/aria-setsize-expected-mac.txt b/content/test/data/accessibility/aria/aria-setsize-expected-mac.txt new file mode 100644 index 0000000..36811b7e --- /dev/null +++ b/content/test/data/accessibility/aria/aria-setsize-expected-mac.txt
@@ -0,0 +1,12 @@ +AXWebArea AXRoleDescription='HTML content' +++AXList AXRoleDescription='list' +++++AXStaticText AXRoleDescription='text' AXTitle='Item 1' AXARIASetSize='4' AXARIAPosInSet='1' +++++AXStaticText AXRoleDescription='text' AXTitle='Item 2' AXARIASetSize='4' AXARIAPosInSet='2' +++++AXStaticText AXRoleDescription='text' AXTitle='Item 3' AXARIASetSize='4' AXARIAPosInSet='3' +++++AXStaticText AXRoleDescription='text' AXTitle='Item 4' AXARIASetSize='4' AXARIAPosInSet='4' +++AXList AXRoleDescription='list' +++++AXStaticText AXRoleDescription='text' AXTitle='Item 1' AXARIASetSize='5' AXARIAPosInSet='1' +++++AXStaticText AXRoleDescription='text' AXTitle='Item 2' AXARIASetSize='5' AXARIAPosInSet='2' +++++AXStaticText AXRoleDescription='text' AXTitle='Item 3' AXARIASetSize='5' AXARIAPosInSet='3' +++++AXStaticText AXRoleDescription='text' AXTitle='Item 4' AXARIASetSize='5' AXARIAPosInSet='4' +++++AXStaticText AXRoleDescription='text' AXTitle='Item 5' AXARIASetSize='5' AXARIAPosInSet='5'
diff --git a/content/test/data/accessibility/aria/aria-setsize-expected-win.txt b/content/test/data/accessibility/aria/aria-setsize-expected-win.txt new file mode 100644 index 0000000..6cae42b --- /dev/null +++ b/content/test/data/accessibility/aria/aria-setsize-expected-win.txt
@@ -0,0 +1,12 @@ +ROLE_SYSTEM_DOCUMENT READONLY FOCUSABLE +++ROLE_SYSTEM_LIST +++++ROLE_SYSTEM_LISTITEM name='Item 1' FOCUSABLE setsize:4 posinset:1 +++++ROLE_SYSTEM_LISTITEM name='Item 2' FOCUSABLE setsize:4 posinset:2 +++++ROLE_SYSTEM_LISTITEM name='Item 3' FOCUSABLE setsize:4 posinset:3 +++++ROLE_SYSTEM_LISTITEM name='Item 4' FOCUSABLE setsize:4 posinset:4 +++ROLE_SYSTEM_LIST +++++ROLE_SYSTEM_LISTITEM name='Item 1' FOCUSABLE setsize:5 posinset:1 +++++ROLE_SYSTEM_LISTITEM name='Item 2' FOCUSABLE setsize:5 posinset:2 +++++ROLE_SYSTEM_LISTITEM name='Item 3' FOCUSABLE setsize:5 posinset:3 +++++ROLE_SYSTEM_LISTITEM name='Item 4' FOCUSABLE setsize:5 posinset:4 +++++ROLE_SYSTEM_LISTITEM name='Item 5' FOCUSABLE setsize:5 posinset:5
diff --git a/content/test/data/accessibility/aria/aria-setsize.html b/content/test/data/accessibility/aria/aria-setsize.html new file mode 100644 index 0000000..e9ffd1c --- /dev/null +++ b/content/test/data/accessibility/aria/aria-setsize.html
@@ -0,0 +1,23 @@ +<!-- +@MAC-ALLOW:AXRole* +@MAC-ALLOW:AXARIA* +@WIN-ALLOW:setsize* +@WIN-ALLOW:posinset* +--> +<html> +<body> +<div role="listbox"> + <div tabIndex="0" aria-setsize="4" aria-posinset="1" role="option">Item 1</div> + <div tabIndex="0" aria-setsize="4" aria-posinset="2" role="option">Item 2</div> + <div tabIndex="0" aria-setsize="4" aria-posinset="3" role="option">Item 3</div> + <div tabIndex="0" aria-setsize="4" aria-posinset="4" role="option">Item 4</div> +</div> +<div role="listbox"> + <div tabIndex="0" role="option">Item 1</div> + <div tabIndex="0" role="option">Item 2</div> + <div tabIndex="0" role="option">Item 3</div> + <div tabIndex="0" role="option">Item 4</div> + <div tabIndex="0" role="option">Item 5</div> +</div> +</body> +</html>
diff --git a/content/test/fake_compositor_dependencies.cc b/content/test/fake_compositor_dependencies.cc index ee533a4..98aa6b5 100644 --- a/content/test/fake_compositor_dependencies.cc +++ b/content/test/fake_compositor_dependencies.cc
@@ -80,7 +80,8 @@ return &gpu_memory_buffer_manager_; } -RendererScheduler* FakeCompositorDependencies::GetRendererScheduler() { +scheduler::RendererScheduler* +FakeCompositorDependencies::GetRendererScheduler() { return &renderer_scheduler_; }
diff --git a/content/test/fake_compositor_dependencies.h b/content/test/fake_compositor_dependencies.h index 7c6606b..0dac57b7 100644 --- a/content/test/fake_compositor_dependencies.h +++ b/content/test/fake_compositor_dependencies.h
@@ -36,7 +36,7 @@ GetCompositorImplThreadTaskRunner() override; cc::SharedBitmapManager* GetSharedBitmapManager() override; gpu::GpuMemoryBufferManager* GetGpuMemoryBufferManager() override; - RendererScheduler* GetRendererScheduler() override; + scheduler::RendererScheduler* GetRendererScheduler() override; cc::ContextProvider* GetSharedMainThreadContextProvider() override; scoped_ptr<cc::BeginFrameSource> CreateExternalBeginFrameSource( int routing_id) override;
diff --git a/content/test/fake_renderer_scheduler.cc b/content/test/fake_renderer_scheduler.cc index 07c85a5..1424706 100644 --- a/content/test/fake_renderer_scheduler.cc +++ b/content/test/fake_renderer_scheduler.cc
@@ -27,7 +27,7 @@ return nullptr; } -scoped_refptr<SingleThreadIdleTaskRunner> +scoped_refptr<scheduler::SingleThreadIdleTaskRunner> FakeRendererScheduler::IdleTaskRunner() { return nullptr; }
diff --git a/content/test/fake_renderer_scheduler.h b/content/test/fake_renderer_scheduler.h index d1d39de..7de65cee 100644 --- a/content/test/fake_renderer_scheduler.h +++ b/content/test/fake_renderer_scheduler.h
@@ -5,11 +5,11 @@ #ifndef CONTENT_TEST_FAKE_RENDERER_SCHEDULER_H_ #define CONTENT_TEST_FAKE_RENDERER_SCHEDULER_H_ -#include "content/renderer/scheduler/renderer_scheduler.h" +#include "components/scheduler/renderer/renderer_scheduler.h" namespace content { -class FakeRendererScheduler : public RendererScheduler { +class FakeRendererScheduler : public scheduler::RendererScheduler { public: FakeRendererScheduler(); ~FakeRendererScheduler() override; @@ -18,7 +18,8 @@ scoped_refptr<base::SingleThreadTaskRunner> DefaultTaskRunner() override; scoped_refptr<base::SingleThreadTaskRunner> CompositorTaskRunner() override; scoped_refptr<base::SingleThreadTaskRunner> LoadingTaskRunner() override; - scoped_refptr<SingleThreadIdleTaskRunner> IdleTaskRunner() override; + scoped_refptr<scheduler::SingleThreadIdleTaskRunner> IdleTaskRunner() + override; scoped_refptr<base::SingleThreadTaskRunner> TimerTaskRunner() override; void WillBeginFrame(const cc::BeginFrameArgs& args) override; void BeginFrameNotExpectedSoon() override;
diff --git a/content/test/gpu/OWNERS b/content/test/gpu/OWNERS index 84f8217..74fe357 100644 --- a/content/test/gpu/OWNERS +++ b/content/test/gpu/OWNERS
@@ -1,2 +1,3 @@ +bajones@chromium.org kbr@chromium.org zmo@chromium.org
diff --git a/content/test/gpu/gpu_tests/webgl_conformance_expectations.py b/content/test/gpu/gpu_tests/webgl_conformance_expectations.py index d91520ed..522911e 100644 --- a/content/test/gpu/gpu_tests/webgl_conformance_expectations.py +++ b/content/test/gpu/gpu_tests/webgl_conformance_expectations.py
@@ -9,14 +9,6 @@ class WebGLConformanceExpectations(GpuTestExpectations): def SetExpectations(self): # Fails on all platforms - self.Fail('conformance/glsl/misc/shaders-with-invariance.html', - bug=421710) - self.Fail('conformance/glsl/bugs/essl3-shaders-with-webgl1.html', - bug=428845) - self.Fail('conformance/glsl/misc/expression-list-in-declarator-initializer.html', - bug=428845) - self.Fail('conformance/uniforms/gl-uniform-arrays.html', - bug=433385) self.Fail( 'conformance/ogles/GL/biuDepthRange/biuDepthRange_001_to_002.html', bug=478570) @@ -34,23 +26,10 @@ bug=478572) # Win failures - self.Fail('conformance/glsl/misc/struct-equals.html', - ['win'], bug=391957) - self.Fail('conformance/glsl/bugs/conditional-discard-in-loop.html', - ['win'], bug=402195) self.Fail('conformance/glsl/misc/ternary-operators-in-global-initializers.html', ['win'], bug=415694) self.Fail('conformance/glsl/misc/struct-specifiers-in-uniforms.html', ['win'], bug=433412) - # This test still causes itself and any tests afterwards to time out - # in Win Debug bots. - self.Skip('conformance/textures/texture-copying-feedback-loops.html', - ['Win'], bug=421695) - - self.Fail('conformance/rendering/framebuffer-switch.html', - ['win'], bug=428849) - self.Fail('conformance/rendering/framebuffer-texture-switch.html', - ['win'], bug=428849) # Win7 / Intel failures self.Fail('conformance/rendering/gl-scissor-test.html', @@ -64,10 +43,6 @@ self.Fail('conformance/glsl/misc/shader-with-array-of-structs-uniform.html', ['win7', 'intel', 'nvidia'], bug=373972) - # Win8 / NVIDIA failures - self.Fail('conformance/textures/tex-image-and-sub-image-2d-with-array-buffer-view.html', - ['win', 'nvidia'], bug=459265) - # Win / AMD failures self.Fail('conformance/textures/texparameter-test.html', ['win', 'amd', 'd3d9'], bug=839) # angle bug ID @@ -83,6 +58,10 @@ self.Skip('conformance/extensions/oes-texture-half-float-with-canvas.html', ['win', 'd3d9'], bug=896) # angle bug ID + # Mac failures + self.Fail('conformance/glsl/misc/shaders-with-invariance.html', + ['mac'], bug=421710) + # Mac / Intel failures # Radar 13499466 self.Fail('conformance/limits/gl-max-texture-dimensions.html', @@ -118,6 +97,10 @@ 'conformance/glsl/bugs/array-of-struct-with-int-first-position.html', ['mac', ('nvidia', 0xfd5), ('nvidia', 0xfe9)], bug=368912) + # Mac / AMD Failures + self.Fail('deqp/data/gles2/shaders/conversions.html', + ['mac', 'amd'], bug=478572) + # Mac 10.8 / ATI failures self.Fail( 'conformance/rendering/' + @@ -201,6 +184,8 @@ self.Fail('conformance/more/functions/texSubImage2DHTML.html', ['linux', ('amd', 0x68f9)], bug=436212) # AMD Radeon 6450 + self.Fail('conformance/extensions/angle-instanced-arrays.html', + ['linux', ('amd', 0x6779)], bug=479260) self.Fail('conformance/extensions/ext-texture-filter-anisotropic.html', ['linux', ('amd', 0x6779)], bug=436212) self.Fail('conformance/glsl/misc/shader-struct-scope.html', @@ -215,6 +200,15 @@ ['linux', ('amd', 0x6779)], bug=436212) # Android failures + self.Fail('deqp/data/gles2/shaders/constants.html', + ['android'], bug=478572) + self.Fail('deqp/data/gles2/shaders/conversions.html', + ['android'], bug=478572) + self.Fail('deqp/data/gles2/shaders/declarations.html', + ['android'], bug=478572) + self.Fail('deqp/data/gles2/shaders/linkage.html', + ['android'], bug=478572) + # The following test is very slow and therefore times out on Android bot. self.Skip('conformance/rendering/multisample-corruption.html', ['android'])
diff --git a/content/test/test_blink_web_unit_test_support.cc b/content/test/test_blink_web_unit_test_support.cc index 3b24701..07f53ea 100644 --- a/content/test/test_blink_web_unit_test_support.cc +++ b/content/test/test_blink_web_unit_test_support.cc
@@ -9,8 +9,8 @@ #include "base/files/scoped_temp_dir.h" #include "base/path_service.h" #include "base/strings/utf_string_conversions.h" -#include "content/renderer/scheduler/renderer_scheduler.h" -#include "content/renderer/scheduler/webthread_impl_for_renderer_scheduler.h" +#include "components/scheduler/renderer/renderer_scheduler.h" +#include "components/scheduler/renderer/webthread_impl_for_renderer_scheduler.h" #include "content/test/mock_webclipboard_impl.h" #include "content/test/web_gesture_curve_mock.h" #include "content/test/web_layer_tree_view_impl_for_testing.h" @@ -56,9 +56,9 @@ #endif if (base::MessageLoopProxy::current()) { - renderer_scheduler_ = RendererScheduler::Create(); - web_thread_.reset( - new WebThreadImplForRendererScheduler(renderer_scheduler_.get())); + renderer_scheduler_ = scheduler::RendererScheduler::Create(); + web_thread_.reset(new scheduler::WebThreadImplForRendererScheduler( + renderer_scheduler_.get())); } blink::initialize(this);
diff --git a/content/test/test_blink_web_unit_test_support.h b/content/test/test_blink_web_unit_test_support.h index 0b27e7b..66761cf 100644 --- a/content/test/test_blink_web_unit_test_support.h +++ b/content/test/test_blink_web_unit_test_support.h
@@ -24,8 +24,11 @@ class WebLayerTreeView; } -namespace content { +namespace scheduler { class RendererScheduler; +} + +namespace content { // An implementation of blink::WebUnitTestSupport and BlinkPlatformImpl for // tests. @@ -94,7 +97,7 @@ base::ScopedTempDir file_system_root_; scoped_ptr<WebURLLoaderMockFactory> url_loader_factory_; cc_blink::WebCompositorSupportImpl compositor_support_; - scoped_ptr<RendererScheduler> renderer_scheduler_; + scoped_ptr<scheduler::RendererScheduler> renderer_scheduler_; scoped_ptr<blink::WebThread> web_thread_; #if defined(OS_WIN) || defined(OS_MACOSX)
diff --git a/content/test/test_frame_navigation_observer.cc b/content/test/test_frame_navigation_observer.cc index c6d151bc..e3cbccecd 100644 --- a/content/test/test_frame_navigation_observer.cc +++ b/content/test/test_frame_navigation_observer.cc
@@ -67,6 +67,7 @@ ++navigations_completed_; if (navigations_completed_ == number_of_navigations_) { + load_committed_details_ = details; navigation_started_ = false; message_loop_runner_->Quit(); }
diff --git a/content/test/test_frame_navigation_observer.h b/content/test/test_frame_navigation_observer.h index 9ff42c53..5330c48 100644 --- a/content/test/test_frame_navigation_observer.h +++ b/content/test/test_frame_navigation_observer.h
@@ -10,6 +10,7 @@ #include "base/callback.h" #include "base/compiler_specific.h" #include "base/memory/scoped_ptr.h" +#include "content/public/browser/navigation_details.h" #include "content/public/browser/web_contents_observer.h" #include "content/public/test/test_utils.h" @@ -19,7 +20,6 @@ class FrameTreeNode; class RenderFrameHostImpl; class WebContents; -struct LoadCommittedDetails; // For content_browsertests, which run on the UI thread, run a second // MessageLoop and quit when the navigation in a specific frame completes @@ -38,6 +38,11 @@ // navigations are complete. void Wait(); + // Returns the LoadCommittedDetails for the last navigation to commit. + const LoadCommittedDetails& load_committed_details() const { + return load_committed_details_; + } + private: // WebContentsObserver void DidStartProvisionalLoadForFrame(RenderFrameHost* render_frame_host, @@ -60,6 +65,8 @@ // The number of navigations to wait for. int number_of_navigations_; + LoadCommittedDetails load_committed_details_; + // The MessageLoopRunner used to spin the message loop. scoped_refptr<MessageLoopRunner> message_loop_runner_;
diff --git a/crypto/BUILD.gn b/crypto/BUILD.gn index 9f944e5b..27e786c 100644 --- a/crypto/BUILD.gn +++ b/crypto/BUILD.gn
@@ -136,9 +136,6 @@ "ec_signature_creator_nss.cc", "encryptor_nss.cc", "hmac_nss.cc", - "nss_util.cc", - "nss_util.h", - "nss_util_internal.h", "rsa_private_key_nss.cc", "secure_hash_default.cc", "signature_creator_nss.cc", @@ -170,6 +167,16 @@ ] } + # Remove nss_util when NSS is used for neither the internal crypto library + # nor the platform certificate library. + if (use_openssl && !use_nss_certs) { + sources -= [ + "nss_util.cc", + "nss_util.h", + "nss_util_internal.h", + ] + } + defines = [ "CRYPTO_IMPLEMENTATION" ] } @@ -205,53 +212,54 @@ } } -# TODO(GYP): Make this link on win as well. -if (!is_win) { - test("crypto_unittests") { - sources = [ - # Tests. - "curve25519_unittest.cc", - "ec_private_key_unittest.cc", - "ec_signature_creator_unittest.cc", - "encryptor_unittest.cc", - "ghash_unittest.cc", - "hkdf_unittest.cc", - "hmac_unittest.cc", - "nss_util_unittest.cc", - "openssl_bio_string_unittest.cc", - "p224_spake_unittest.cc", - "p224_unittest.cc", - "random_unittest.cc", - "rsa_private_key_nss_unittest.cc", - "rsa_private_key_unittest.cc", - "secure_hash_unittest.cc", - "sha2_unittest.cc", - "signature_creator_unittest.cc", - "signature_verifier_unittest.cc", - "symmetric_key_unittest.cc", - ] +test("crypto_unittests") { + sources = [ + # Tests. + "curve25519_unittest.cc", + "ec_private_key_unittest.cc", + "ec_signature_creator_unittest.cc", + "encryptor_unittest.cc", + "ghash_unittest.cc", + "hkdf_unittest.cc", + "hmac_unittest.cc", + "nss_util_unittest.cc", + "openssl_bio_string_unittest.cc", + "p224_spake_unittest.cc", + "p224_unittest.cc", + "random_unittest.cc", + "rsa_private_key_nss_unittest.cc", + "rsa_private_key_unittest.cc", + "secure_hash_unittest.cc", + "sha2_unittest.cc", + "signature_creator_unittest.cc", + "signature_verifier_unittest.cc", + "symmetric_key_unittest.cc", + ] - if (use_openssl || !is_linux) { - sources -= [ "rsa_private_key_nss_unittest.cc" ] - } - - if (use_openssl) { - sources -= [ "nss_util_unittest.cc" ] - } else { - sources -= [ "openssl_bio_string_unittest.cc" ] - } - - deps = [ - ":crypto", - ":platform", - ":test_support", - "//base", - "//base/test:run_all_unittests", - "//base/test:test_support", - "//testing/gmock", - "//testing/gtest", - ] + # Remove nss_util when NSS is used for neither the internal crypto library + # nor the platform certificate library. + if (use_openssl && !use_nss_certs) { + sources -= [ "nss_util_unittest.cc" ] } + + if (use_openssl) { + sources -= [ "rsa_private_key_nss_unittest.cc" ] + } else { + sources -= [ "openssl_bio_string_unittest.cc" ] + } + + configs += [ "//build/config/compiler:no_size_t_to_int_warning" ] + + deps = [ + ":crypto", + ":platform", + ":test_support", + "//base", + "//base/test:run_all_unittests", + "//base/test:test_support", + "//testing/gmock", + "//testing/gtest", + ] } source_set("test_support") { @@ -287,7 +295,7 @@ } config("platform_config") { - if (!use_openssl && is_clang) { + if ((!use_openssl || use_nss_certs) && is_clang) { # There is a broken header guard in /usr/include/nss/secmod.h: # https://bugzilla.mozilla.org/show_bug.cgi?id=884072 cflags = [ "-Wno-header-guard" ] @@ -306,21 +314,26 @@ deps = [ "//net/third_party/nss/ssl:libssl", ] + } + + # Link in NSS if it is used for either the internal crypto library + # (!use_openssl) or platform certificate library (use_nss_certs). + if (!use_openssl || use_nss_certs) { if (is_linux) { # On Linux, we use the system NSS (excepting SSL where we always use our # own). - # - # We always need our SSL header search path to come before the system one - # so our versions are used. The libssl target will add the search path we - # want, but according to GN's ordering rules, public_configs' search path - # will get applied before ones inherited from our dependencies. - # Therefore, we need to explicitly list our custom libssl's config here - # before the system one. - public_configs = [ - ":platform_config", - "//net/third_party/nss/ssl:ssl_config", - "//third_party/nss:system_nss_no_ssl_config", - ] + public_configs = [ ":platform_config" ] + if (!use_openssl) { + # If using a bundled copy of NSS's SSL library, ensure the bundled SSL + # header search path comes before the system one so our versions are + # used. The libssl target will add the search path we want, but + # according to GN's ordering rules, public_configs' search path will get + # applied before ones inherited from our dependencies. Therefore, we + # need to explicitly list our custom libssl's config here before the + # system one. + public_configs += [ "//net/third_party/nss/ssl:ssl_config" ] + } + public_configs += [ "//third_party/nss:system_nss_no_ssl_config" ] } else { # Non-Linux platforms use the hermetic NSS from the tree. deps += [
diff --git a/crypto/crypto.gyp b/crypto/crypto.gyp index c8551e70..a9cff553 100644 --- a/crypto/crypto.gyp +++ b/crypto/crypto.gyp
@@ -108,9 +108,6 @@ 'ec_signature_creator_nss.cc', 'encryptor_nss.cc', 'hmac_nss.cc', - 'nss_util.cc', - 'nss_util.h', - 'nss_util_internal.h', 'rsa_private_key_nss.cc', 'secure_hash_default.cc', 'signature_creator_nss.cc', @@ -143,6 +140,15 @@ 'symmetric_key_openssl.cc', ], },], + [ 'use_openssl==1 and use_nss_certs==0', { + # NSS is used for neither the internal crypto library nor the + # platform certificate library. + 'sources!': [ + 'nss_util.cc', + 'nss_util.h', + 'nss_util_internal.h', + ], + },], ], 'sources': [ '<@(crypto_sources)', @@ -182,7 +188,7 @@ '../testing/gtest.gyp:gtest', ], 'conditions': [ - [ 'os_posix == 1 and OS != "mac" and OS != "android" and OS != "ios"', { + [ 'use_nss_certs == 1', { 'conditions': [ [ 'use_allocator!="none"', { 'dependencies': [ @@ -194,10 +200,13 @@ 'dependencies': [ '../build/linux/system.gyp:ssl', ], - }, { # os_posix != 1 or OS == "mac" or OS == "android" or OS == "ios" + }], + [ 'use_openssl == 1 and use_nss_certs == 0', { + # nss_util is built if NSS is used for either the internal crypto + # library or the platform certificate library. 'sources!': [ - 'rsa_private_key_nss_unittest.cc', - ] + 'nss_util_unittest.cc', + ], }], [ 'use_openssl == 0 and (OS == "mac" or OS == "ios" or OS == "win")', { 'dependencies': [ @@ -213,7 +222,6 @@ '../third_party/boringssl/boringssl.gyp:boringssl', ], 'sources!': [ - 'nss_util_unittest.cc', 'rsa_private_key_nss_unittest.cc', ], }, {
diff --git a/crypto/crypto_nacl.gyp b/crypto/crypto_nacl.gyp index 4451610..255c42c 100644 --- a/crypto/crypto_nacl.gyp +++ b/crypto/crypto_nacl.gyp
@@ -23,7 +23,6 @@ }, 'dependencies': [ '../third_party/boringssl/boringssl_nacl.gyp:boringssl_nacl', - '../native_client/tools.gyp:prep_toolchain', '../native_client_sdk/native_client_sdk_untrusted.gyp:nacl_io_untrusted', ], 'defines': [
diff --git a/crypto/rsa_private_key.h b/crypto/rsa_private_key.h index 78a660e..9ab9c57 100644 --- a/crypto/rsa_private_key.h +++ b/crypto/rsa_private_key.h
@@ -180,7 +180,22 @@ static RSAPrivateKey* CreateFromPrivateKeyInfo( const std::vector<uint8>& input); -#if defined(USE_NSS_CERTS) +#if defined(USE_OPENSSL) + // Create a new instance from an existing EVP_PKEY, taking a + // reference to it. |key| must be an RSA key. Returns NULL on + // failure. + static RSAPrivateKey* CreateFromKey(EVP_PKEY* key); +#else + // Create a new instance by referencing an existing private key + // structure. Does not import the key. + static RSAPrivateKey* CreateFromKey(SECKEYPrivateKey* key); +#endif + + // TODO(davidben): These functions are used when NSS is the platform key + // store, but they also assume that the internal crypto library is NSS. Split + // out the convenience NSS platform key methods from the logic which expects + // an RSAPrivateKey. See https://crbug.com/478777. +#if defined(USE_NSS_CERTS) && !defined(USE_OPENSSL) // Create a new random instance in |slot|. Can return NULL if initialization // fails. The created key is permanent and is not exportable in plaintext // form. @@ -194,10 +209,6 @@ PK11SlotInfo* slot, const std::vector<uint8>& input); - // Create a new instance by referencing an existing private key - // structure. Does not import the key. - static RSAPrivateKey* CreateFromKey(SECKEYPrivateKey* key); - // Import an existing public key, and then search for the private // half in the key database. The format of the public key blob is is // an X509 SubjectPublicKeyInfo block. This can return NULL if @@ -216,13 +227,7 @@ static RSAPrivateKey* FindFromPublicKeyInfoInSlot( const std::vector<uint8>& input, PK11SlotInfo* slot); -#elif defined(USE_OPENSSL) - // Create a new instance from an existing EVP_PKEY, taking a - // reference to it. |key| must be an RSA key. Returns NULL on - // failure. - static RSAPrivateKey* CreateFromKey(EVP_PKEY* key); - -#endif +#endif // USE_NSS_CERTS && !USE_OPENSSL #if defined(USE_OPENSSL) EVP_PKEY* key() { return key_; }
diff --git a/crypto/rsa_private_key_nss.cc b/crypto/rsa_private_key_nss.cc index 45b2be76..c9e6a87f 100644 --- a/crypto/rsa_private_key_nss.cc +++ b/crypto/rsa_private_key_nss.cc
@@ -104,6 +104,22 @@ false /* not sensitive */); } +// static +RSAPrivateKey* RSAPrivateKey::CreateFromKey(SECKEYPrivateKey* key) { + DCHECK(key); + if (SECKEY_GetPrivateKeyType(key) != rsaKey) + return NULL; + RSAPrivateKey* copy = new RSAPrivateKey(); + copy->key_ = SECKEY_CopyPrivateKey(key); + copy->public_key_ = SECKEY_ConvertToPublicKey(key); + if (!copy->key_ || !copy->public_key_) { + NOTREACHED(); + delete copy; + return NULL; + } + return copy; +} + #if defined(USE_NSS_CERTS) // static RSAPrivateKey* RSAPrivateKey::CreateSensitive(PK11SlotInfo* slot, @@ -125,22 +141,6 @@ } // static -RSAPrivateKey* RSAPrivateKey::CreateFromKey(SECKEYPrivateKey* key) { - DCHECK(key); - if (SECKEY_GetPrivateKeyType(key) != rsaKey) - return NULL; - RSAPrivateKey* copy = new RSAPrivateKey(); - copy->key_ = SECKEY_CopyPrivateKey(key); - copy->public_key_ = SECKEY_ConvertToPublicKey(key); - if (!copy->key_ || !copy->public_key_) { - NOTREACHED(); - delete copy; - return NULL; - } - return copy; -} - -// static RSAPrivateKey* RSAPrivateKey::FindFromPublicKeyInfo( const std::vector<uint8>& input) { scoped_ptr<RSAPrivateKey> result(InitPublicPart(input));
diff --git a/crypto/rsa_private_key_nss_unittest.cc b/crypto/rsa_private_key_nss_unittest.cc index 98360e8..dad6688 100644 --- a/crypto/rsa_private_key_nss_unittest.cc +++ b/crypto/rsa_private_key_nss_unittest.cc
@@ -13,6 +13,10 @@ namespace crypto { +// TODO(davidben): These tests assume NSS is used for both the internal crypto +// library and the platform key store. See https://crbug.com/478777. +#if defined(USE_NSS_CERTS) + class RSAPrivateKeyNSSTest : public testing::Test { public: RSAPrivateKeyNSSTest() {} @@ -57,4 +61,6 @@ EXPECT_EQ(NULL, crypto::RSAPrivateKey::FindFromPublicKeyInfo(public_key)); } +#endif // USE_NSS_CERTS + } // namespace crypto
diff --git a/crypto/rsa_private_key_unittest.cc b/crypto/rsa_private_key_unittest.cc index ee5b1217..b231cac 100644 --- a/crypto/rsa_private_key_unittest.cc +++ b/crypto/rsa_private_key_unittest.cc
@@ -445,9 +445,6 @@ input2.size())); } -// The following test can run if either USE_NSS_CERTS or USE_OPENSSL is defined, -// but not otherwise (since it uses crypto::RSAPrivateKey::CreateFromKey). -#if defined(USE_NSS_CERTS) || defined(USE_OPENSSL) TEST(RSAPrivateKeyUnitTest, CreateFromKeyTest) { scoped_ptr<crypto::RSAPrivateKey> key_pair( crypto::RSAPrivateKey::Create(256)); @@ -469,5 +466,4 @@ ASSERT_EQ(privkey, privkey_copy); ASSERT_EQ(pubkey, pubkey_copy); } -#endif
diff --git a/dbus/mock_object_proxy.h b/dbus/mock_object_proxy.h index 7c61b47..66f485a 100644 --- a/dbus/mock_object_proxy.h +++ b/dbus/mock_object_proxy.h
@@ -29,7 +29,7 @@ Response*(MethodCall* method_call, int timeout_ms, ScopedDBusError* error)); - virtual scoped_ptr<Response> CallMethodAndBlockWithErrorDetails( + scoped_ptr<Response> CallMethodAndBlockWithErrorDetails( MethodCall* method_call, int timeout_ms, ScopedDBusError* error) override { @@ -38,8 +38,8 @@ } MOCK_METHOD2(MockCallMethodAndBlock, Response*(MethodCall* method_call, int timeout_ms)); - virtual scoped_ptr<Response> CallMethodAndBlock(MethodCall* method_call, - int timeout_ms) override { + scoped_ptr<Response> CallMethodAndBlock(MethodCall* method_call, + int timeout_ms) override { return scoped_ptr<Response>(MockCallMethodAndBlock(method_call, timeout_ms)); } @@ -58,7 +58,7 @@ MOCK_METHOD0(Detach, void()); protected: - virtual ~MockObjectProxy(); + ~MockObjectProxy() override; }; } // namespace dbus
diff --git a/device/bluetooth/bluetooth_adapter.cc b/device/bluetooth/bluetooth_adapter.cc index fd2e29b3..23fde08 100644 --- a/device/bluetooth/bluetooth_adapter.cc +++ b/device/bluetooth/bluetooth_adapter.cc
@@ -24,22 +24,30 @@ } #endif // !defined(OS_CHROMEOS) && !defined(OS_WIN) && !defined(OS_MACOSX) +base::WeakPtr<BluetoothAdapter> BluetoothAdapter::GetWeakPtrForTesting() { + return weak_ptr_factory_.GetWeakPtr(); +} + #if defined(OS_CHROMEOS) void BluetoothAdapter::Shutdown() { NOTIMPLEMENTED(); } #endif -BluetoothAdapter::BluetoothAdapter() - : weak_ptr_factory_(this) { +void BluetoothAdapter::AddObserver(BluetoothAdapter::Observer* observer) { + DCHECK(observer); + observers_.AddObserver(observer); } -BluetoothAdapter::~BluetoothAdapter() { - STLDeleteValues(&devices_); +void BluetoothAdapter::RemoveObserver(BluetoothAdapter::Observer* observer) { + DCHECK(observer); + observers_.RemoveObserver(observer); } -base::WeakPtr<BluetoothAdapter> BluetoothAdapter::GetWeakPtrForTesting() { - return weak_ptr_factory_.GetWeakPtr(); +void BluetoothAdapter::StartDiscoverySession( + const DiscoverySessionCallback& callback, + const ErrorCallback& error_callback) { + StartDiscoverySessionWithFilter(nullptr, callback, error_callback); } void BluetoothAdapter::StartDiscoverySessionWithFilter( @@ -53,50 +61,6 @@ error_callback); } -void BluetoothAdapter::StartDiscoverySession( - const DiscoverySessionCallback& callback, - const ErrorCallback& error_callback) { - StartDiscoverySessionWithFilter(nullptr, callback, error_callback); -} - -scoped_ptr<BluetoothDiscoveryFilter> -BluetoothAdapter::GetMergedDiscoveryFilterHelper( - const BluetoothDiscoveryFilter* masked_filter, - bool omit) const { - scoped_ptr<BluetoothDiscoveryFilter> result; - bool first_merge = true; - - std::set<BluetoothDiscoverySession*> temp(discovery_sessions_); - for (const auto& iter : temp) { - const BluetoothDiscoveryFilter* curr_filter = iter->GetDiscoveryFilter(); - - if (!iter->IsActive()) - continue; - - if (omit && curr_filter == masked_filter) { - // if masked_filter is pointing to empty filter, and there are - // multiple empty filters in discovery_sessions_, make sure we'll - // process next empty sessions. - omit = false; - continue; - } - - if (first_merge) { - first_merge = false; - if (curr_filter) { - result.reset(new BluetoothDiscoveryFilter( - BluetoothDiscoveryFilter::Transport::TRANSPORT_DUAL)); - result->CopyFrom(*curr_filter); - } - continue; - } - - result = BluetoothDiscoveryFilter::Merge(result.get(), curr_filter); - } - - return result.Pass(); -} - scoped_ptr<BluetoothDiscoveryFilter> BluetoothAdapter::GetMergedDiscoveryFilter() const { return GetMergedDiscoveryFilterHelper(nullptr, false); @@ -183,6 +147,13 @@ return pairing_delegates_.front().first; } +BluetoothAdapter::BluetoothAdapter() : weak_ptr_factory_(this) { +} + +BluetoothAdapter::~BluetoothAdapter() { + STLDeleteValues(&devices_); +} + void BluetoothAdapter::OnStartDiscoverySession( scoped_ptr<BluetoothDiscoveryFilter> discovery_filter, const DiscoverySessionCallback& callback) { @@ -213,4 +184,42 @@ discovery_sessions_.erase(discovery_session); } +scoped_ptr<BluetoothDiscoveryFilter> +BluetoothAdapter::GetMergedDiscoveryFilterHelper( + const BluetoothDiscoveryFilter* masked_filter, + bool omit) const { + scoped_ptr<BluetoothDiscoveryFilter> result; + bool first_merge = true; + + std::set<BluetoothDiscoverySession*> temp(discovery_sessions_); + for (const auto& iter : temp) { + const BluetoothDiscoveryFilter* curr_filter = iter->GetDiscoveryFilter(); + + if (!iter->IsActive()) + continue; + + if (omit && curr_filter == masked_filter) { + // if masked_filter is pointing to empty filter, and there are + // multiple empty filters in discovery_sessions_, make sure we'll + // process next empty sessions. + omit = false; + continue; + } + + if (first_merge) { + first_merge = false; + if (curr_filter) { + result.reset(new BluetoothDiscoveryFilter( + BluetoothDiscoveryFilter::Transport::TRANSPORT_DUAL)); + result->CopyFrom(*curr_filter); + } + continue; + } + + result = BluetoothDiscoveryFilter::Merge(result.get(), curr_filter); + } + + return result.Pass(); +} + } // namespace device
diff --git a/device/bluetooth/bluetooth_adapter.h b/device/bluetooth/bluetooth_adapter.h index d764ae97..87734d2 100644 --- a/device/bluetooth/bluetooth_adapter.h +++ b/device/bluetooth/bluetooth_adapter.h
@@ -36,8 +36,7 @@ // known to the adapter, discovering new devices, and providing notification of // updates to device information. class DEVICE_BLUETOOTH_EXPORT BluetoothAdapter - : public base::RefCountedThreadSafe<BluetoothAdapter, - BluetoothAdapterDeleter> { + : public base::RefCounted<BluetoothAdapter> { public: // Interface for observing changes from bluetooth adapters. class Observer { @@ -181,6 +180,17 @@ // initialization, if initialization is asynchronous on the platform. typedef base::Callback<void()> InitCallback; + typedef base::Callback<void(scoped_ptr<BluetoothDiscoverySession>)> + DiscoverySessionCallback; + typedef std::vector<BluetoothDevice*> DeviceList; + typedef std::vector<const BluetoothDevice*> ConstDeviceList; + typedef base::Callback<void(scoped_refptr<BluetoothSocket>)> + CreateServiceCallback; + typedef base::Callback<void(const std::string& message)> + CreateServiceErrorCallback; + typedef base::Callback<void(scoped_refptr<BluetoothAudioSink>)> + AcquiredCallback; + // Returns a weak pointer to a new adapter. For platforms with asynchronous // initialization, the returned adapter will run the |init_callback| once // asynchronous initialization is complete. @@ -204,9 +214,8 @@ // Adds and removes observers for events on this bluetooth adapter. If // monitoring multiple adapters, check the |adapter| parameter of observer // methods to determine which adapter is issuing the event. - virtual void AddObserver(BluetoothAdapter::Observer* observer) = 0; - virtual void RemoveObserver( - BluetoothAdapter::Observer* observer) = 0; + virtual void AddObserver(BluetoothAdapter::Observer* observer); + virtual void RemoveObserver(BluetoothAdapter::Observer* observer); // The address of this adapter. The address format is "XX:XX:XX:XX:XX:XX", // where each XX is a hexadecimal number. @@ -270,8 +279,6 @@ // that have been discovered so far. Otherwise, clients can be notified of all // new and lost devices by implementing the Observer methods "DeviceAdded" and // "DeviceRemoved". - typedef base::Callback<void(scoped_ptr<BluetoothDiscoverySession>)> - DiscoverySessionCallback; virtual void StartDiscoverySession(const DiscoverySessionCallback& callback, const ErrorCallback& error_callback); virtual void StartDiscoverySessionWithFilter( @@ -291,9 +298,7 @@ // Requests the list of devices from the adapter. All devices are returned, // including those currently connected and those paired. Use the returned // device pointers to determine which they are. - typedef std::vector<BluetoothDevice*> DeviceList; virtual DeviceList GetDevices(); - typedef std::vector<const BluetoothDevice*> ConstDeviceList; virtual ConstDeviceList GetDevices() const; // Returns a pointer to the device with the given address |address| or NULL if @@ -335,10 +340,6 @@ // called on success with a BluetoothSocket instance that is to be owned by // the received. |error_callback| will be called on failure with a message // indicating the cause. - typedef base::Callback<void(scoped_refptr<BluetoothSocket>)> - CreateServiceCallback; - typedef base::Callback<void(const std::string& message)> - CreateServiceErrorCallback; virtual void CreateRfcommService( const BluetoothUUID& uuid, const ServiceOptions& options, @@ -363,24 +364,22 @@ // will be called on success with a BluetoothAudioSink which is to be owned by // the caller of this method. |error_callback| will be called on failure with // a message indicating the cause. - typedef base::Callback<void(scoped_refptr<BluetoothAudioSink>)> - AcquiredCallback; virtual void RegisterAudioSink( const BluetoothAudioSink::Options& options, const AcquiredCallback& callback, const BluetoothAudioSink::ErrorCallback& error_callback) = 0; protected: - friend class base::RefCountedThreadSafe<BluetoothAdapter, - BluetoothAdapterDeleter>; - friend struct BluetoothAdapterDeleter; + friend class base::RefCounted<BluetoothAdapter>; friend class BluetoothDiscoverySession; + + typedef std::map<const std::string, BluetoothDevice*> DevicesMap; + typedef std::pair<BluetoothDevice::PairingDelegate*, PairingDelegatePriority> + PairingDelegatePair; + BluetoothAdapter(); virtual ~BluetoothAdapter(); - // Called by RefCountedThreadSafeDeleteOnCorrectThread to destroy this. - virtual void DeleteOnCorrectThread() const = 0; - // Internal methods for initiating and terminating device discovery sessions. // An implementation of BluetoothAdapter keeps an internal reference count to // make sure that the underlying controller is constantly searching for nearby @@ -448,16 +447,16 @@ void DiscoverySessionBecameInactive( BluetoothDiscoverySession* discovery_session); + // Observers of BluetoothAdapter, notified from implementation subclasses. + ObserverList<device::BluetoothAdapter::Observer> observers_; + // Devices paired with, connected to, discovered by, or visible to the // adapter. The key is the Bluetooth address of the device and the value is // the BluetoothDevice object whose lifetime is managed by the adapter // instance. - typedef std::map<const std::string, BluetoothDevice*> DevicesMap; DevicesMap devices_; // Default pairing delegates registered with the adapter. - typedef std::pair<BluetoothDevice::PairingDelegate*, - PairingDelegatePriority> PairingDelegatePair; std::list<PairingDelegatePair> pairing_delegates_; private: @@ -480,13 +479,6 @@ base::WeakPtrFactory<BluetoothAdapter> weak_ptr_factory_; }; -// Trait for RefCountedThreadSafe that deletes BluetoothAdapter. -struct BluetoothAdapterDeleter { - static void Destruct(const BluetoothAdapter* adapter) { - adapter->DeleteOnCorrectThread(); - } -}; - } // namespace device #endif // DEVICE_BLUETOOTH_BLUETOOTH_ADAPTER_H_
diff --git a/device/bluetooth/bluetooth_adapter_chromeos.cc b/device/bluetooth/bluetooth_adapter_chromeos.cc index b9cdd09..26a029bd 100644 --- a/device/bluetooth/bluetooth_adapter_chromeos.cc +++ b/device/bluetooth/bluetooth_adapter_chromeos.cc
@@ -138,24 +138,6 @@ Shutdown(); } -void BluetoothAdapterChromeOS::DeleteOnCorrectThread() const { - if (ui_task_runner_->RunsTasksOnCurrentThread() || - !ui_task_runner_->DeleteSoon(FROM_HERE, this)) - delete this; -} - -void BluetoothAdapterChromeOS::AddObserver( - BluetoothAdapter::Observer* observer) { - DCHECK(observer); - observers_.AddObserver(observer); -} - -void BluetoothAdapterChromeOS::RemoveObserver( - BluetoothAdapter::Observer* observer) { - DCHECK(observer); - observers_.RemoveObserver(observer); -} - std::string BluetoothAdapterChromeOS::GetAddress() const { if (!IsPresent()) return std::string();
diff --git a/device/bluetooth/bluetooth_adapter_chromeos.h b/device/bluetooth/bluetooth_adapter_chromeos.h index cc5fb1d..7b17f53 100644 --- a/device/bluetooth/bluetooth_adapter_chromeos.h +++ b/device/bluetooth/bluetooth_adapter_chromeos.h
@@ -65,9 +65,6 @@ // BluetoothAdapter: void Shutdown() override; - void DeleteOnCorrectThread() const override; - void AddObserver(device::BluetoothAdapter::Observer* observer) override; - void RemoveObserver(device::BluetoothAdapter::Observer* observer) override; std::string GetAddress() const override; std::string GetName() const override; void SetName(const std::string& name, @@ -156,7 +153,6 @@ device::BluetoothDevice::PairingDelegate* pairing_delegate) override; private: - friend class base::DeleteHelper<BluetoothAdapterChromeOS>; friend class BluetoothChromeOSTest; friend class BluetoothChromeOSTest_Shutdown_Test; friend class BluetoothChromeOSTest_Shutdown_OnStartDiscovery_Test; @@ -172,6 +168,10 @@ ErrorCallback> DiscoveryParamTuple; typedef std::queue<DiscoveryParamTuple> DiscoveryCallbackQueue; + // Callback pair for the profile registration queue. + typedef std::pair<base::Closure, ErrorCompletionCallback> + RegisterProfileCompletionPair; + BluetoothAdapterChromeOS(); ~BluetoothAdapterChromeOS() override; @@ -349,9 +349,6 @@ // Object path of the adapter we track. dbus::ObjectPath object_path_; - // List of observers interested in event notifications from us. - ObserverList<device::BluetoothAdapter::Observer> observers_; - // Instance of the D-Bus agent object used for pairing, initialized with // our own class as its delegate. scoped_ptr<BluetoothAgentServiceProvider> agent_; @@ -363,10 +360,6 @@ // The profiles we have registered with the bluetooth daemon. std::map<device::BluetoothUUID, BluetoothAdapterProfileChromeOS*> profiles_; - // Callback pair for the profile registration queue. - typedef std::pair<base::Closure, ErrorCompletionCallback> - RegisterProfileCompletionPair; - // Queue of delegates waiting for a profile to register. std::map<device::BluetoothUUID, std::vector<RegisterProfileCompletionPair>*> profile_queues_;
diff --git a/device/bluetooth/bluetooth_adapter_mac.h b/device/bluetooth/bluetooth_adapter_mac.h index bca4389..14d5a2b 100644 --- a/device/bluetooth/bluetooth_adapter_mac.h +++ b/device/bluetooth/bluetooth_adapter_mac.h
@@ -41,8 +41,6 @@ static base::WeakPtr<BluetoothAdapter> CreateAdapter(); // BluetoothAdapter: - void AddObserver(BluetoothAdapter::Observer* observer) override; - void RemoveObserver(BluetoothAdapter::Observer* observer) override; std::string GetAddress() const override; std::string GetName() const override; void SetName(const std::string& name, @@ -87,14 +85,12 @@ device::BluetoothDevice::PairingDelegate* pairing_delegate) override; private: - friend class base::DeleteHelper<BluetoothAdapterMac>; friend class BluetoothAdapterMacTest; BluetoothAdapterMac(); ~BluetoothAdapterMac() override; // BluetoothAdapter: - void DeleteOnCorrectThread() const override; void AddDiscoverySession(BluetoothDiscoveryFilter* discovery_filter, const base::Closure& callback, const ErrorCallback& error_callback) override; @@ -129,9 +125,6 @@ scoped_refptr<base::SequencedTaskRunner> ui_task_runner_; - // List of observers interested in event notifications from us. - ObserverList<BluetoothAdapter::Observer> observers_; - base::WeakPtrFactory<BluetoothAdapterMac> weak_ptr_factory_; DISALLOW_COPY_AND_ASSIGN(BluetoothAdapterMac);
diff --git a/device/bluetooth/bluetooth_adapter_mac.mm b/device/bluetooth/bluetooth_adapter_mac.mm index 7c04a433..a528bd7 100644 --- a/device/bluetooth/bluetooth_adapter_mac.mm +++ b/device/bluetooth/bluetooth_adapter_mac.mm
@@ -64,16 +64,6 @@ BluetoothAdapterMac::~BluetoothAdapterMac() { } -void BluetoothAdapterMac::AddObserver(BluetoothAdapter::Observer* observer) { - DCHECK(observer); - observers_.AddObserver(observer); -} - -void BluetoothAdapterMac::RemoveObserver(BluetoothAdapter::Observer* observer) { - DCHECK(observer); - observers_.RemoveObserver(observer); -} - std::string BluetoothAdapterMac::GetAddress() const { return address_; } @@ -173,12 +163,6 @@ DeviceAdded(device); } -void BluetoothAdapterMac::DeleteOnCorrectThread() const { - if (ui_task_runner_->RunsTasksOnCurrentThread() || - !ui_task_runner_->DeleteSoon(FROM_HERE, this)) - delete this; -} - void BluetoothAdapterMac::AddDiscoverySession( BluetoothDiscoveryFilter* discovery_filter, const base::Closure& callback,
diff --git a/device/bluetooth/bluetooth_adapter_unittest.cc b/device/bluetooth/bluetooth_adapter_unittest.cc index 81e2dcb..c028b97 100644 --- a/device/bluetooth/bluetooth_adapter_unittest.cc +++ b/device/bluetooth/bluetooth_adapter_unittest.cc
@@ -19,10 +19,6 @@ TestBluetoothAdapter() { } - void AddObserver(BluetoothAdapter::Observer* observer) override {} - - void RemoveObserver(BluetoothAdapter::Observer* observer) override {} - std::string GetAddress() const override { return ""; } std::string GetName() const override { return ""; } @@ -49,8 +45,6 @@ bool IsDiscovering() const override { return false; } - void DeleteOnCorrectThread() const override { delete this; } - void StartDiscoverySessionWithFilter( scoped_ptr<BluetoothDiscoveryFilter> discovery_filter, const DiscoverySessionCallback& callback,
diff --git a/device/bluetooth/bluetooth_adapter_win.cc b/device/bluetooth/bluetooth_adapter_win.cc index a9fc20034..4f49788 100644 --- a/device/bluetooth/bluetooth_adapter_win.cc +++ b/device/bluetooth/bluetooth_adapter_win.cc
@@ -53,16 +53,6 @@ } } -void BluetoothAdapterWin::AddObserver(BluetoothAdapter::Observer* observer) { - DCHECK(observer); - observers_.AddObserver(observer); -} - -void BluetoothAdapterWin::RemoveObserver(BluetoothAdapter::Observer* observer) { - DCHECK(observer); - observers_.RemoveObserver(observer); -} - std::string BluetoothAdapterWin::GetAddress() const { return address_; } @@ -291,12 +281,6 @@ } } -void BluetoothAdapterWin::DeleteOnCorrectThread() const { - if (ui_task_runner_->RunsTasksOnCurrentThread() || - !ui_task_runner_->DeleteSoon(FROM_HERE, this)) - delete this; -} - // If the method is called when |discovery_status_| is DISCOVERY_STOPPING, // starting again is handled by BluetoothAdapterWin::DiscoveryStopped(). void BluetoothAdapterWin::AddDiscoverySession(
diff --git a/device/bluetooth/bluetooth_adapter_win.h b/device/bluetooth/bluetooth_adapter_win.h index 2b086a2f..3bb26c9 100644 --- a/device/bluetooth/bluetooth_adapter_win.h +++ b/device/bluetooth/bluetooth_adapter_win.h
@@ -41,8 +41,6 @@ const InitCallback& init_callback); // BluetoothAdapter: - virtual void AddObserver(BluetoothAdapter::Observer* observer) override; - virtual void RemoveObserver(BluetoothAdapter::Observer* observer) override; virtual std::string GetAddress() const override; virtual std::string GetName() const override; virtual void SetName(const std::string& name, @@ -97,7 +95,6 @@ device::BluetoothDevice::PairingDelegate* pairing_delegate) override; private: - friend class base::DeleteHelper<BluetoothAdapterWin>; friend class BluetoothAdapterWinTest; enum DiscoveryStatus { @@ -111,7 +108,6 @@ virtual ~BluetoothAdapterWin(); // BluetoothAdapter: - void DeleteOnCorrectThread() const override; virtual void AddDiscoverySession( BluetoothDiscoveryFilter* discovery_filter, const base::Closure& callback, @@ -152,9 +148,6 @@ base::ThreadChecker thread_checker_; - // List of observers interested in event notifications from us. - ObserverList<BluetoothAdapter::Observer> observers_; - // NOTE: This should remain the last member so it'll be destroyed and // invalidate its weak pointers before any other members are destroyed. base::WeakPtrFactory<BluetoothAdapterWin> weak_ptr_factory_;
diff --git a/device/bluetooth/bluetooth_device.h b/device/bluetooth/bluetooth_device.h index f1abf31..1b9b3a9 100644 --- a/device/bluetooth/bluetooth_device.h +++ b/device/bluetooth/bluetooth_device.h
@@ -97,6 +97,8 @@ ERROR_UNSUPPORTED_DEVICE }; + typedef std::vector<BluetoothUUID> UUIDList; + // Interface for negotiating pairing of bluetooth devices. class PairingDelegate { public: @@ -254,7 +256,6 @@ // devices this data is collected from both the EIR data and SDP tables, // for Low Energy devices this data is collected from AD and GATT primary // services, for dual mode devices this may be collected from both./ - typedef std::vector<BluetoothUUID> UUIDList; virtual UUIDList GetUUIDs() const = 0; // The ErrorCallback is used for methods that can fail in which case it
diff --git a/device/bluetooth/bluetooth_device_win.h b/device/bluetooth/bluetooth_device_win.h index ee36d37d..9a6edd5 100644 --- a/device/bluetooth/bluetooth_device_win.h +++ b/device/bluetooth/bluetooth_device_win.h
@@ -92,6 +92,8 @@ private: friend class BluetoothAdapterWin; + typedef ScopedVector<BluetoothServiceRecordWin> ServiceRecordList; + // Used by BluetoothAdapterWin to update the visible state during // discovery. void SetVisible(bool visible); @@ -127,7 +129,6 @@ UUIDList uuids_; // The service records retrieved from SDP. - typedef ScopedVector<BluetoothServiceRecordWin> ServiceRecordList; ServiceRecordList service_record_list_; DISALLOW_COPY_AND_ASSIGN(BluetoothDeviceWin);
diff --git a/device/bluetooth/bluetooth_gatt_descriptor.h b/device/bluetooth/bluetooth_gatt_descriptor.h index 442a6ae..c91de1c 100644 --- a/device/bluetooth/bluetooth_gatt_descriptor.h +++ b/device/bluetooth/bluetooth_gatt_descriptor.h
@@ -21,6 +21,14 @@ // characteristic's features or to control certain behaviors. class DEVICE_BLUETOOTH_EXPORT BluetoothGattDescriptor { public: + // The ErrorCallback is used by methods to asynchronously report errors. + typedef base::Callback<void(BluetoothGattService::GattErrorCode)> + ErrorCallback; + + // The ValueCallback is used to return the value of a remote characteristic + // descriptor upon a read request. + typedef base::Callback<void(const std::vector<uint8>&)> ValueCallback; + // The Bluetooth Specification declares several predefined descriptors that // profiles can use. The following are definitions for the list of UUIDs // and descriptions of the characteristic descriptors that they represent. @@ -106,14 +114,6 @@ // handled by the subsystem. static const BluetoothUUID& CharacteristicAggregateFormatUuid(); - // The ErrorCallback is used by methods to asynchronously report errors. - typedef base::Callback<void(BluetoothGattService::GattErrorCode)> - ErrorCallback; - - // The ValueCallback is used to return the value of a remote characteristic - // descriptor upon a read request. - typedef base::Callback<void(const std::vector<uint8>&)> ValueCallback; - // Constructs a BluetoothGattDescriptor that can be associated with a local // GATT characteristic when the adapter is in the peripheral role. To // associate the returned descriptor with a characteristic, add it to a local
diff --git a/device/bluetooth/bluetooth_remote_gatt_characteristic_chromeos.h b/device/bluetooth/bluetooth_remote_gatt_characteristic_chromeos.h index 077aff1..a23a1178 100644 --- a/device/bluetooth/bluetooth_remote_gatt_characteristic_chromeos.h +++ b/device/bluetooth/bluetooth_remote_gatt_characteristic_chromeos.h
@@ -70,6 +70,9 @@ private: friend class BluetoothRemoteGattServiceChromeOS; + typedef std::pair<NotifySessionCallback, ErrorCallback> + PendingStartNotifyCall; + BluetoothRemoteGattCharacteristicChromeOS( BluetoothRemoteGattServiceChromeOS* service, const dbus::ObjectPath& object_path); @@ -121,8 +124,6 @@ // Calls to StartNotifySession that are pending. This can happen during the // first remote call to start notifications. - typedef std::pair<NotifySessionCallback, ErrorCallback> - PendingStartNotifyCall; std::queue<PendingStartNotifyCall> pending_start_notify_calls_; // True, if a Start or Stop notify call to bluetoothd is currently pending.
diff --git a/device/bluetooth/bluetooth_remote_gatt_service_chromeos.h b/device/bluetooth/bluetooth_remote_gatt_service_chromeos.h index 6694553..9aa8fdd 100644 --- a/device/bluetooth/bluetooth_remote_gatt_service_chromeos.h +++ b/device/bluetooth/bluetooth_remote_gatt_service_chromeos.h
@@ -96,6 +96,9 @@ private: friend class BluetoothDeviceChromeOS; + typedef std::map<dbus::ObjectPath, BluetoothRemoteGattCharacteristicChromeOS*> + CharacteristicMap; + BluetoothRemoteGattServiceChromeOS(BluetoothAdapterChromeOS* adapter, BluetoothDeviceChromeOS* device, const dbus::ObjectPath& object_path); @@ -127,8 +130,6 @@ // owned by this service. Since the Chrome OS implementation uses object // paths as unique identifiers, we also use this mapping to return // characteristics by identifier. - typedef std::map<dbus::ObjectPath, BluetoothRemoteGattCharacteristicChromeOS*> - CharacteristicMap; CharacteristicMap characteristics_; // Indicates whether or not the characteristics of this service are known to
diff --git a/device/bluetooth/test/mock_bluetooth_adapter.cc b/device/bluetooth/test/mock_bluetooth_adapter.cc index a021bfd..615239b 100644 --- a/device/bluetooth/test/mock_bluetooth_adapter.cc +++ b/device/bluetooth/test/mock_bluetooth_adapter.cc
@@ -19,10 +19,6 @@ } #endif -void MockBluetoothAdapter::DeleteOnCorrectThread() const { - delete this; -}; - void MockBluetoothAdapter::AddDiscoverySession( BluetoothDiscoveryFilter* discovery_filter, const base::Closure& callback, @@ -39,6 +35,7 @@ scoped_ptr<BluetoothDiscoveryFilter> discovery_filter, const base::Closure& callback, const ErrorCallback& error_callback) { + SetDiscoveryFilterRaw(discovery_filter.get(), callback, error_callback); } void MockBluetoothAdapter::StartDiscoverySessionWithFilter(
diff --git a/device/bluetooth/test/mock_bluetooth_adapter.h b/device/bluetooth/test/mock_bluetooth_adapter.h index 3abe858..37f6450 100644 --- a/device/bluetooth/test/mock_bluetooth_adapter.h +++ b/device/bluetooth/test/mock_bluetooth_adapter.h
@@ -65,6 +65,10 @@ void(const BluetoothDiscoveryFilter*, const DiscoverySessionCallback& callback, const ErrorCallback& error_callback)); + MOCK_METHOD3(SetDiscoveryFilterRaw, + void(const BluetoothDiscoveryFilter*, + const base::Closure& callback, + const ErrorCallback& error_callback)); MOCK_CONST_METHOD0(GetDevices, BluetoothAdapter::ConstDeviceList()); MOCK_METHOD1(GetDevice, BluetoothDevice*(const std::string& address)); MOCK_CONST_METHOD1(GetDevice, @@ -96,7 +100,6 @@ const ErrorCallback& error_callback); protected: - void DeleteOnCorrectThread() const override; void AddDiscoverySession(BluetoothDiscoveryFilter* discovery_filter, const base::Closure& callback, const ErrorCallback& error_callback) override;
diff --git a/device/bluetooth/test/mock_bluetooth_discovery_session.cc b/device/bluetooth/test/mock_bluetooth_discovery_session.cc index 1aea85a..08f3dae 100644 --- a/device/bluetooth/test/mock_bluetooth_discovery_session.cc +++ b/device/bluetooth/test/mock_bluetooth_discovery_session.cc
@@ -21,4 +21,11 @@ } MockBluetoothDiscoverySession::~MockBluetoothDiscoverySession() {} +void MockBluetoothDiscoverySession::SetDiscoveryFilter( + scoped_ptr<BluetoothDiscoveryFilter> discovery_filter, + const base::Closure& callback, + const ErrorCallback& error_callback) { + SetDiscoveryFilterRaw(discovery_filter.get(), callback, error_callback); +} + } // namespace device
diff --git a/device/bluetooth/test/mock_bluetooth_discovery_session.h b/device/bluetooth/test/mock_bluetooth_discovery_session.h index bd724a7..e9009a8 100644 --- a/device/bluetooth/test/mock_bluetooth_discovery_session.h +++ b/device/bluetooth/test/mock_bluetooth_discovery_session.h
@@ -20,6 +20,15 @@ MOCK_METHOD2(Stop, void(const base::Closure& callback, const ErrorCallback& error_callback)); + MOCK_METHOD3(SetDiscoveryFilterRaw, + void(BluetoothDiscoveryFilter* discovery_filter, + const base::Closure& callback, + const ErrorCallback& error_callback)); + + protected: + void SetDiscoveryFilter(scoped_ptr<BluetoothDiscoveryFilter> discovery_filter, + const base::Closure& callback, + const ErrorCallback& error_callback) override; private: DISALLOW_COPY_AND_ASSIGN(MockBluetoothDiscoverySession);
diff --git a/device/test/data/bluetooth/invalid_uuid.xml b/device/test/data/bluetooth/invalid_uuid.xml deleted file mode 100644 index 2b333047..0000000 --- a/device/test/data/bluetooth/invalid_uuid.xml +++ /dev/null
@@ -1,8 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" ?> -<record> - <attribute id="0x0001"> - <sequence> - <uuid value="01234567:89AB-CDEF-0123-456789ABCDEF" /> - </sequence> - </attribute> -</record>
diff --git a/device/test/data/bluetooth/medium_uuid.xml b/device/test/data/bluetooth/medium_uuid.xml deleted file mode 100644 index 432d7fe..0000000 --- a/device/test/data/bluetooth/medium_uuid.xml +++ /dev/null
@@ -1,8 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" ?> -<record> - <attribute id="0x0001"> - <sequence> - <uuid value="0x00001101" /> - </sequence> - </attribute> -</record>
diff --git a/device/test/data/bluetooth/rfcomm.xml b/device/test/data/bluetooth/rfcomm.xml deleted file mode 100644 index ec3bdec..0000000 --- a/device/test/data/bluetooth/rfcomm.xml +++ /dev/null
@@ -1,39 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" ?> - -<record> - <attribute id="0x0000"> - <uint32 value="0x00010003" /> - </attribute> - <attribute id="0x0001"> - <sequence> - <uuid value="01234567-89ab-cdef-0123-456789abcdef" /> - </sequence> - </attribute> - <attribute id="0x0004"> - <sequence> - <sequence> - <uuid value="0x0100" /> - </sequence> - <sequence> - <uuid value="0x0003" /> - <uint8 value="0x0c" /> - </sequence> - </sequence> - </attribute> - <attribute id="0x0005"> - <sequence> - <uuid value="0x1002" /> - </sequence> - </attribute> - <attribute id="0x0009"> - <sequence> - <sequence> - <uuid value="0x1108" /> - <uint16 value="0x0102" /> - </sequence> - </sequence> - </attribute> - <attribute id="0x0100"> - <text value="Headset Audio Gateway" /> - </attribute> -</record>
diff --git a/device/test/data/bluetooth/short_uuid.xml b/device/test/data/bluetooth/short_uuid.xml deleted file mode 100644 index 9ad3c9f5..0000000 --- a/device/test/data/bluetooth/short_uuid.xml +++ /dev/null
@@ -1,8 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" ?> -<record> - <attribute id="0x0001"> - <sequence> - <uuid value="0x1101" /> - </sequence> - </attribute> -</record>
diff --git a/device/test/data/bluetooth/uppercase_uuid.xml b/device/test/data/bluetooth/uppercase_uuid.xml deleted file mode 100644 index 4e0574f..0000000 --- a/device/test/data/bluetooth/uppercase_uuid.xml +++ /dev/null
@@ -1,8 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" ?> -<record> - <attribute id="0x0001"> - <sequence> - <uuid value="01234567-89AB-CDEF-0123-456789ABCDEF" /> - </sequence> - </attribute> -</record>
diff --git a/device/usb/usb_device_impl.cc b/device/usb/usb_device_impl.cc index f04092f9..0d9ac46 100644 --- a/device/usb/usb_device_impl.cc +++ b/device/usb/usb_device_impl.cc
@@ -99,6 +99,7 @@ const base::string16& manufacturer_string, const base::string16& product_string, const base::string16& serial_number, + const std::string& device_node, scoped_refptr<base::SequencedTaskRunner> blocking_task_runner) : UsbDevice(vendor_id, product_id, @@ -107,6 +108,9 @@ product_string, serial_number), platform_device_(platform_device), +#if defined(OS_CHROMEOS) + devnode_(device_node), +#endif // defined(OS_CHROMEOS) context_(context), task_runner_(base::ThreadTaskRunnerHandle::Get()), blocking_task_runner_(blocking_task_runner) {
diff --git a/device/usb/usb_device_impl.h b/device/usb/usb_device_impl.h index a865ea21..71edf1f0 100644 --- a/device/usb/usb_device_impl.h +++ b/device/usb/usb_device_impl.h
@@ -56,6 +56,7 @@ const base::string16& manufacturer_string, const base::string16& product_string, const base::string16& serial_number, + const std::string& device_node, scoped_refptr<base::SequencedTaskRunner> blocking_task_runner); ~UsbDeviceImpl() override;
diff --git a/device/usb/usb_service_impl.cc b/device/usb/usb_service_impl.cc index ce5b51a4..bd737c7 100644 --- a/device/usb/usb_service_impl.cc +++ b/device/usb/usb_service_impl.cc
@@ -537,7 +537,7 @@ scoped_refptr<UsbDeviceImpl> device( new UsbDeviceImpl(context_, platform_device, vendor_id, product_id, unique_id, manufacturer_string, product_string, - serial_number, blocking_task_runner_)); + serial_number, device_node, blocking_task_runner_)); platform_devices_[platform_device] = device; devices_[unique_id] = device;
diff --git a/extensions/browser/api/bluetooth/bluetooth_private_api.cc b/extensions/browser/api/bluetooth/bluetooth_private_api.cc index 27cc124..1fd4870 100644 --- a/extensions/browser/api/bluetooth/bluetooth_private_api.cc +++ b/extensions/browser/api/bluetooth/bluetooth_private_api.cc
@@ -9,13 +9,13 @@ #include "base/strings/string_util.h" #include "device/bluetooth/bluetooth_adapter.h" #include "device/bluetooth/bluetooth_adapter_factory.h" +#include "device/bluetooth/bluetooth_discovery_session.h" #include "extensions/browser/api/bluetooth/bluetooth_api.h" #include "extensions/browser/api/bluetooth/bluetooth_event_router.h" #include "extensions/common/api/bluetooth_private.h" namespace bt_private = extensions::core_api::bluetooth_private; -namespace SetDiscoveryFilter = - extensions::core_api::bluetooth_private::SetDiscoveryFilter; +namespace SetDiscoveryFilter = bt_private::SetDiscoveryFilter; namespace extensions { @@ -347,16 +347,71 @@ SendResponse(false); } +void BluetoothPrivateSetDiscoveryFilterFunction::OnSuccessCallback() { + SendResponse(true); +} + +void BluetoothPrivateSetDiscoveryFilterFunction::OnErrorCallback() { + SetError(kSetDiscoveryFilterFailed); + SendResponse(false); +} + bool BluetoothPrivateSetDiscoveryFilterFunction::DoWork( scoped_refptr<device::BluetoothAdapter> adapter) { scoped_ptr<SetDiscoveryFilter::Params> params( SetDiscoveryFilter::Params::Create(*args_)); + auto& df_param = params->discovery_filter; - // TODO(jpawlowski): parse arguments, and call event router when method is - // implemented. + scoped_ptr<device::BluetoothDiscoveryFilter> discovery_filter; - SetError(kSetDiscoveryFilterFailed); - SendResponse(false); + // If all filter fields are empty, we are clearing filter. If any field is + // set, then create proper filter. + if (df_param.uuids.get() || df_param.rssi.get() || df_param.pathloss.get() || + df_param.transport != bt_private::TransportType::TRANSPORT_TYPE_NONE) { + uint8_t transport; + + switch (df_param.transport) { + case bt_private::TransportType::TRANSPORT_TYPE_LE: + transport = device::BluetoothDiscoveryFilter::Transport::TRANSPORT_LE; + break; + case bt_private::TransportType::TRANSPORT_TYPE_BREDR: + transport = + device::BluetoothDiscoveryFilter::Transport::TRANSPORT_CLASSIC; + break; + default: // TRANSPORT_TYPE_NONE is included here + transport = device::BluetoothDiscoveryFilter::Transport::TRANSPORT_DUAL; + break; + } + + discovery_filter.reset(new device::BluetoothDiscoveryFilter(transport)); + + if (df_param.uuids.get()) { + std::vector<device::BluetoothUUID> uuids; + if (df_param.uuids->as_string.get()) { + discovery_filter->AddUUID( + device::BluetoothUUID(*df_param.uuids->as_string)); + } else if (df_param.uuids->as_strings.get()) { + for (const auto& iter : *df_param.uuids->as_strings) { + discovery_filter->AddUUID(device::BluetoothUUID(iter)); + } + } + } + + if (df_param.rssi.get()) + discovery_filter->SetRSSI(*df_param.rssi); + + if (df_param.pathloss.get()) + discovery_filter->SetPathloss(*df_param.pathloss); + } + + BluetoothAPI::Get(browser_context())->event_router()->SetDiscoveryFilter( + discovery_filter.Pass(), adapter.get(), extension_id(), + base::Bind( + &BluetoothPrivateSetDiscoveryFilterFunction::OnSuccessCallback, + this), + base::Bind( + &BluetoothPrivateSetDiscoveryFilterFunction::OnErrorCallback, + this)); return true; }
diff --git a/extensions/browser/api/bluetooth/bluetooth_private_apitest.cc b/extensions/browser/api/bluetooth/bluetooth_private_apitest.cc index 203d749..85d88be8 100644 --- a/extensions/browser/api/bluetooth/bluetooth_private_apitest.cc +++ b/extensions/browser/api/bluetooth/bluetooth_private_apitest.cc
@@ -8,6 +8,7 @@ #include "chrome/browser/extensions/extension_apitest.h" #include "device/bluetooth/test/mock_bluetooth_adapter.h" #include "device/bluetooth/test/mock_bluetooth_device.h" +#include "device/bluetooth/test/mock_bluetooth_discovery_session.h" #include "extensions/browser/api/bluetooth/bluetooth_api.h" #include "extensions/browser/api/bluetooth/bluetooth_event_router.h" #include "extensions/browser/event_router.h" @@ -15,9 +16,13 @@ #include "extensions/common/switches.h" #include "testing/gmock/include/gmock/gmock.h" +using device::BluetoothDiscoveryFilter; +using device::BluetoothUUID; using device::MockBluetoothAdapter; using device::MockBluetoothDevice; +using device::MockBluetoothDiscoverySession; using testing::_; +using testing::Eq; using testing::InSequence; using testing::NiceMock; using testing::Return; @@ -31,10 +36,13 @@ namespace extensions { namespace { - const char kTestExtensionId[] = "jofgjdphhceggjecimellaapdjjadibj"; const char kAdapterName[] = "Helix"; const char kDeviceName[] = "Red"; + +MATCHER_P(IsFilterEqual, a, "") { + return arg->Equals(*a); +} } class BluetoothPrivateApiTest : public ExtensionApiTest { @@ -111,6 +119,14 @@ DispatchPairingEvent(bt_private::PAIRING_EVENT_TYPE_REQUESTPASSKEY); } + void CallSetDiscoveryFilterCallback( + device::BluetoothAdapter::DiscoverySessionCallback callback) { + auto session_ptr = scoped_ptr<NiceMock<MockBluetoothDiscoverySession>>( + mock_discovery_session_); + + callback.Run(session_ptr.Pass()); + } + protected: std::string adapter_name_; bool adapter_powered_; @@ -118,6 +134,10 @@ scoped_refptr<NiceMock<MockBluetoothAdapter> > mock_adapter_; scoped_ptr<NiceMock<MockBluetoothDevice> > mock_device_; + + // This discovery session will be owned by EventRouter, we'll only keep + // pointer to it. + NiceMock<MockBluetoothDiscoverySession>* mock_discovery_session_; }; ACTION_TEMPLATE(InvokeCallbackArgument, @@ -205,4 +225,30 @@ << message_; } +IN_PROC_BROWSER_TEST_F(BluetoothPrivateApiTest, DiscoveryFilter) { + mock_discovery_session_ = new NiceMock<MockBluetoothDiscoverySession>(); + + BluetoothDiscoveryFilter discovery_filter( + BluetoothDiscoveryFilter::Transport::TRANSPORT_LE); + discovery_filter.SetPathloss(50); + discovery_filter.AddUUID(BluetoothUUID("cafe")); + discovery_filter.AddUUID( + BluetoothUUID("0000bebe-0000-1000-8000-00805f9b34fb")); + + EXPECT_CALL(*mock_adapter_.get(), StartDiscoverySessionWithFilterRaw( + IsFilterEqual(&discovery_filter), _, _)) + .Times(1) + .WillOnce(WithArgs<1>(Invoke( + this, &BluetoothPrivateApiTest::CallSetDiscoveryFilterCallback))); + EXPECT_CALL(*mock_discovery_session_, IsActive()) + .Times(1) + .WillOnce(Return(true)); + EXPECT_CALL(*mock_discovery_session_, + SetDiscoveryFilterRaw(Eq(nullptr), _, _)) + .Times(1) + .WillOnce(InvokeCallbackArgument<1>()); + ASSERT_TRUE(RunComponentExtensionTest("bluetooth_private/discovery_filter")) + << message_; +} + } // namespace extensions
diff --git a/extensions/browser/api/mime_handler_private/mime_handler_private.cc b/extensions/browser/api/mime_handler_private/mime_handler_private.cc index b7d557d..13caee2 100644 --- a/extensions/browser/api/mime_handler_private/mime_handler_private.cc +++ b/extensions/browser/api/mime_handler_private/mime_handler_private.cc
@@ -4,8 +4,10 @@ #include "extensions/browser/api/mime_handler_private/mime_handler_private.h" +#include "base/strings/string_util.h" #include "content/public/browser/stream_handle.h" #include "content/public/browser/stream_info.h" +#include "content/public/common/content_constants.h" #include "extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.h" #include "extensions/common/constants.h" #include "net/http/http_response_headers.h" @@ -24,6 +26,15 @@ std::string header_name; std::string header_value; while (headers->EnumerateHeaderLines(&iter, &header_name, &header_value)) { + // mojo strings must be UTF-8 and headers might not be, so drop any headers + // that aren't ASCII. The PDF plugin does not use any headers with non-ASCII + // names and non-ASCII values are never useful for the headers the plugin + // does use. + // + // TODO(sammc): Send as bytes instead of a string and let the client decide + // how to decode. + if (!base::IsStringASCII(header_name) || !base::IsStringASCII(header_value)) + continue; auto& current_value = result[header_name]; if (!current_value.empty()) current_value += ", "; @@ -89,7 +100,18 @@ result->tab_id = stream.tab_id(); const content::StreamInfo* info = stream.stream_info(); result->mime_type = info->mime_type; - result->original_url = info->original_url.spec(); + + // If the URL is too long, mojo will give up on sending the URL. In these + // cases truncate it. Only data: URLs should ever really suffer this problem + // so only worry about those for now. + // TODO(raymes): This appears to be a bug in mojo somewhere. crbug.com/480099. + if (info->original_url.SchemeIs(url::kDataScheme) && + info->original_url.spec().size() > content::kMaxURLDisplayChars) { + result->original_url = info->original_url.scheme() + ":"; + } else { + result->original_url = info->original_url.spec(); + } + result->stream_url = info->handle->GetURL().spec(); result->response_headers = extensions::CreateResponseHeadersMap(info->response_headers.get());
diff --git a/extensions/browser/api/networking_private/networking_private_event_router_chromeos.cc b/extensions/browser/api/networking_private/networking_private_event_router_chromeos.cc index 999576d0..aa5df440 100644 --- a/extensions/browser/api/networking_private/networking_private_event_router_chromeos.cc +++ b/extensions/browser/api/networking_private/networking_private_event_router_chromeos.cc
@@ -5,6 +5,7 @@ #include "extensions/browser/api/networking_private/networking_private_event_router.h" #include "base/json/json_writer.h" +#include "chromeos/network/device_state.h" #include "chromeos/network/network_event_log.h" #include "chromeos/network/network_state.h" #include "chromeos/network/network_state_handler.h" @@ -20,6 +21,7 @@ #include "extensions/common/api/networking_private.h" #include "third_party/cros_system_api/dbus/service_constants.h" +using chromeos::DeviceState; using chromeos::NetworkHandler; using chromeos::NetworkPortalDetector; using chromeos::NetworkState; @@ -47,6 +49,7 @@ void NetworkListChanged() override; void DeviceListChanged() override; void NetworkPropertiesUpdated(const NetworkState* network) override; + void DevicePropertiesUpdated(const DeviceState* device) override; // NetworkPortalDetector::Observer overrides: void OnPortalDetectionCompleted( @@ -206,6 +209,21 @@ event_router->BroadcastEvent(extension_event.Pass()); } +void NetworkingPrivateEventRouterImpl::DevicePropertiesUpdated( + const DeviceState* device) { + // DeviceState changes may affect Cellular networks. + if (device->type() != shill::kTypeCellular) + return; + + NetworkStateHandler::NetworkStateList cellular_networks; + NetworkHandler::Get()->network_state_handler()->GetNetworkListByType( + chromeos::NetworkTypePattern::Cellular(), false /* configured_only */, + true /* visible_only */, -1 /* default limit */, &cellular_networks); + for (const NetworkState* network : cellular_networks) { + NetworkPropertiesUpdated(network); + } +} + void NetworkingPrivateEventRouterImpl::OnPortalDetectionCompleted( const NetworkState* network, const NetworkPortalDetector::CaptivePortalState& state) {
diff --git a/extensions/browser/api/web_request/web_request_api_helpers.cc b/extensions/browser/api/web_request/web_request_api_helpers.cc index 160462a..73a8dd8 100644 --- a/extensions/browser/api/web_request/web_request_api_helpers.cc +++ b/extensions/browser/api/web_request/web_request_api_helpers.cc
@@ -218,9 +218,8 @@ // Creates NetLog parameters to indicate that an extension modified a request. // Caller takes ownership of returned value. -base::Value* NetLogModificationCallback( - const EventResponseDelta* delta, - net::NetLog::LogLevel log_level) { +base::Value* NetLogModificationCallback(const EventResponseDelta* delta, + net::NetLogCaptureMode capture_mode) { base::DictionaryValue* dict = new base::DictionaryValue(); dict->SetString("extension_id", delta->extension_id);
diff --git a/extensions/browser/content_hash_reader.cc b/extensions/browser/content_hash_reader.cc index 76c7e60..499587b 100644 --- a/extensions/browser/content_hash_reader.cc +++ b/extensions/browser/content_hash_reader.cc
@@ -53,7 +53,7 @@ // Check that this is a valid resource to verify (i.e., it exists). base::FilePath content_path = extension_root_.Append(relative_path_); - if (!base::PathExists(content_path)) + if (!base::PathExists(content_path) || base::DirectoryExists(content_path)) return false; content_exists_ = true;
diff --git a/extensions/browser/guest_view/extensions_guest_view_message_filter.cc b/extensions/browser/guest_view/extensions_guest_view_message_filter.cc new file mode 100644 index 0000000..215bf816 --- /dev/null +++ b/extensions/browser/guest_view/extensions_guest_view_message_filter.cc
@@ -0,0 +1,178 @@ +// 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 "extensions/browser/guest_view/extensions_guest_view_message_filter.h" + +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/render_frame_host.h" +#include "content/public/browser/render_process_host.h" +#include "content/public/browser/render_view_host.h" +#include "extensions/browser/guest_view/guest_view_base.h" +#include "extensions/browser/guest_view/guest_view_manager.h" +#include "extensions/browser/guest_view/mime_handler_view/mime_handler_view_constants.h" +#include "extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.h" +#include "extensions/browser/guest_view/web_view/web_view_content_script_manager.h" +#include "extensions/browser/guest_view/web_view/web_view_guest.h" +#include "extensions/browser/guest_view/web_view/web_view_renderer_state.h" +#include "extensions/common/guest_view/extensions_guest_view_messages.h" +#include "ipc/ipc_message_macros.h" + +using content::BrowserContext; +using content::BrowserThread; +using content::RenderFrameHost; +using content::WebContents; + +namespace extensions { + +ExtensionsGuestViewMessageFilter::ExtensionsGuestViewMessageFilter( + int render_process_id, + BrowserContext* context) + : BrowserMessageFilter(ExtensionsGuestViewMsgStart), + render_process_id_(render_process_id), + browser_context_(context), + weak_ptr_factory_(this) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); +} + +ExtensionsGuestViewMessageFilter::~ExtensionsGuestViewMessageFilter() { + DCHECK_CURRENTLY_ON(BrowserThread::IO); +} + +void ExtensionsGuestViewMessageFilter::OverrideThreadForMessage( + const IPC::Message& message, + BrowserThread::ID* thread) { + switch (message.type()) { + case ExtensionsGuestViewHostMsg_CreateMimeHandlerViewGuest::ID: + case ExtensionsGuestViewHostMsg_ResizeGuest::ID: + *thread = BrowserThread::UI; + break; + default: + break; + } +} + +void ExtensionsGuestViewMessageFilter::OnDestruct() const { + // Destroy the filter on the IO thread since that's where its weak pointers + // are being used. + BrowserThread::DeleteOnIOThread::Destruct(this); +} + +bool ExtensionsGuestViewMessageFilter::OnMessageReceived( + const IPC::Message& message) { + bool handled = true; + IPC_BEGIN_MESSAGE_MAP(ExtensionsGuestViewMessageFilter, message) + IPC_MESSAGE_HANDLER(ExtensionsGuestViewHostMsg_CanExecuteContentScriptSync, + OnCanExecuteContentScript) + IPC_MESSAGE_HANDLER(ExtensionsGuestViewHostMsg_CreateMimeHandlerViewGuest, + OnCreateMimeHandlerViewGuest) + IPC_MESSAGE_HANDLER(ExtensionsGuestViewHostMsg_ResizeGuest, OnResizeGuest) + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP() + return handled; +} + +void ExtensionsGuestViewMessageFilter::OnCanExecuteContentScript( + int render_view_id, + int script_id, + bool* allowed) { + WebViewRendererState::WebViewInfo info; + WebViewRendererState::GetInstance()->GetInfo(render_process_id_, + render_view_id, &info); + + *allowed = + info.content_script_ids.find(script_id) != info.content_script_ids.end(); +} + +void ExtensionsGuestViewMessageFilter::OnCreateMimeHandlerViewGuest( + int render_frame_id, + const std::string& view_id, + int element_instance_id, + const gfx::Size& element_size) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + // Since we are creating a new guest, we will create a GuestViewManager + // if we don't already have one. + auto manager = GuestViewManager::FromBrowserContext(browser_context_); + DCHECK(manager); + + auto rfh = RenderFrameHost::FromID(render_process_id_, render_frame_id); + auto embedder_web_contents = WebContents::FromRenderFrameHost(rfh); + if (!embedder_web_contents) + return; + + GuestViewManager::WebContentsCreatedCallback callback = + base::Bind( + &ExtensionsGuestViewMessageFilter:: + MimeHandlerViewGuestCreatedCallback, + this, + element_instance_id, + render_process_id_, + render_frame_id, + element_size); + + base::DictionaryValue create_params; + create_params.SetString(mime_handler_view::kViewId, view_id); + create_params.SetInteger(guestview::kElementWidth, element_size.width()); + create_params.SetInteger(guestview::kElementHeight, element_size.height()); + manager->CreateGuest(MimeHandlerViewGuest::Type, + embedder_web_contents, + create_params, + callback); +} + +void ExtensionsGuestViewMessageFilter::OnResizeGuest( + int render_frame_id, + int element_instance_id, + const gfx::Size& new_size) { + auto manager = + GuestViewManager::FromBrowserContextIfAvailable(browser_context_); + // We should have a GuestViewManager at this point. If we don't then the + // embedder is misbehaving. + if (!manager) + return; + + auto guest_web_contents = + manager->GetGuestByInstanceID(render_process_id_, element_instance_id); + auto mhvg = MimeHandlerViewGuest::FromWebContents(guest_web_contents); + if (!mhvg) + return; + + SetSizeParams set_size_params; + set_size_params.enable_auto_size.reset(new bool(false)); + set_size_params.normal_size.reset(new gfx::Size(new_size)); + mhvg->SetSize(set_size_params); +} + +void ExtensionsGuestViewMessageFilter::MimeHandlerViewGuestCreatedCallback( + int element_instance_id, + int embedder_render_process_id, + int embedder_render_frame_id, + const gfx::Size& element_size, + WebContents* web_contents) { + auto guest_view = MimeHandlerViewGuest::FromWebContents(web_contents); + if (!guest_view) + return; + + int guest_instance_id = guest_view->guest_instance_id(); + auto rfh = RenderFrameHost::FromID(embedder_render_process_id, + embedder_render_frame_id); + if (!rfh) + return; + + base::DictionaryValue attach_params; + attach_params.SetInteger(guestview::kElementWidth, element_size.width()); + attach_params.SetInteger(guestview::kElementHeight, element_size.height()); + auto manager = + GuestViewManager::FromBrowserContextIfAvailable(browser_context_); + CHECK(manager); + manager->AttachGuest(embedder_render_process_id, + element_instance_id, + guest_instance_id, + attach_params); + + rfh->Send( + new ExtensionsGuestViewMsg_CreateMimeHandlerViewGuestACK( + element_instance_id)); +} + +} // namespace extensions
diff --git a/extensions/browser/guest_view/extensions_guest_view_message_filter.h b/extensions/browser/guest_view/extensions_guest_view_message_filter.h new file mode 100644 index 0000000..6e07d41 --- /dev/null +++ b/extensions/browser/guest_view/extensions_guest_view_message_filter.h
@@ -0,0 +1,78 @@ +// 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 EXTENSIONS_BROWSER_GUEST_VIEW_EXTENSIONS_GUEST_VIEW_MESSAGE_FILTER_H_ +#define EXTENSIONS_BROWSER_GUEST_VIEW_EXTENSIONS_GUEST_VIEW_MESSAGE_FILTER_H_ + +#include <string> + +#include "base/macros.h" +#include "base/memory/weak_ptr.h" +#include "content/public/browser/browser_message_filter.h" + +namespace content { +class BrowserContext; +class WebContents; +} + +namespace gfx { +class Size; +} + +namespace extensions { + +// This class filters out incoming extensions GuestView-specific IPC messages +// from thw renderer process. It is created on the UI thread. Messages may be +// handled on the IO thread or the UI thread. +class ExtensionsGuestViewMessageFilter : public content::BrowserMessageFilter { + public: + ExtensionsGuestViewMessageFilter(int render_process_id, + content::BrowserContext* context); + + private: + friend class content::BrowserThread; + friend class base::DeleteHelper<ExtensionsGuestViewMessageFilter>; + + ~ExtensionsGuestViewMessageFilter() override; + + // content::BrowserMessageFilter implementation. + void OverrideThreadForMessage(const IPC::Message& message, + content::BrowserThread::ID* thread) override; + void OnDestruct() const override; + bool OnMessageReceived(const IPC::Message& message) override; + + // Message handlers on the UI thread. + void OnCanExecuteContentScript(int render_view_id, + int script_id, + bool* allowed); + + void OnCreateMimeHandlerViewGuest(int render_frame_id, + const std::string& view_id, + int element_instance_id, + const gfx::Size& element_size); + void OnResizeGuest(int render_frame_id, + int element_instance_id, + const gfx::Size& new_size); + + // Runs on UI thread. + void MimeHandlerViewGuestCreatedCallback(int element_instance_id, + int embedder_render_process_id, + int embedder_render_frame_id, + const gfx::Size& element_size, + content::WebContents* web_contents); + + const int render_process_id_; + + // Should only be accessed on the UI thread. + content::BrowserContext* const browser_context_; + + // Weak pointers produced by this factory are bound to the IO thread. + base::WeakPtrFactory<ExtensionsGuestViewMessageFilter> weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(ExtensionsGuestViewMessageFilter); +}; + +} // namespace extensions + +#endif // EXTENSIONS_BROWSER_GUEST_VIEW_EXTENSIONS_GUEST_VIEW_MESSAGE_FILTER_H_
diff --git a/extensions/browser/guest_view/guest_view_base.cc b/extensions/browser/guest_view/guest_view_base.cc index 9a095629..134c326 100644 --- a/extensions/browser/guest_view/guest_view_base.cc +++ b/extensions/browser/guest_view/guest_view_base.cc
@@ -341,6 +341,13 @@ } // static +WebContents* GuestViewBase::GetTopLevelWebContents(WebContents* web_contents) { + while (GuestViewBase* guest = FromWebContents(web_contents)) + web_contents = guest->owner_web_contents(); + return web_contents; +} + +// static bool GuestViewBase::IsGuest(WebContents* web_contents) { return !!GuestViewBase::FromWebContents(web_contents); }
diff --git a/extensions/browser/guest_view/guest_view_base.h b/extensions/browser/guest_view/guest_view_base.h index e342889..7969cb9 100644 --- a/extensions/browser/guest_view/guest_view_base.h +++ b/extensions/browser/guest_view/guest_view_base.h
@@ -65,6 +65,12 @@ static GuestViewBase* From(int owner_process_id, int instance_id); + // Given a |web_contents|, returns the top level owner WebContents. If + // |web_contents| does not belong to a GuestView, it will be returned + // unchanged. + static content::WebContents* GetTopLevelWebContents( + content::WebContents* web_contents); + static bool IsGuest(content::WebContents* web_contents); virtual const char* GetViewType() const = 0;
diff --git a/extensions/browser/guest_view/guest_view_message_filter.cc b/extensions/browser/guest_view/guest_view_message_filter.cc index 7f43a3e..3e833d5 100644 --- a/extensions/browser/guest_view/guest_view_message_filter.cc +++ b/extensions/browser/guest_view/guest_view_message_filter.cc
@@ -10,11 +10,6 @@ #include "content/public/browser/render_view_host.h" #include "extensions/browser/guest_view/guest_view_base.h" #include "extensions/browser/guest_view/guest_view_manager.h" -#include "extensions/browser/guest_view/mime_handler_view/mime_handler_view_constants.h" -#include "extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.h" -#include "extensions/browser/guest_view/web_view/web_view_content_script_manager.h" -#include "extensions/browser/guest_view/web_view/web_view_guest.h" -#include "extensions/browser/guest_view/web_view/web_view_renderer_state.h" #include "extensions/common/guest_view/guest_view_messages.h" #include "ipc/ipc_message_macros.h" @@ -43,8 +38,6 @@ BrowserThread::ID* thread) { switch (message.type()) { case GuestViewHostMsg_AttachGuest::ID: - case GuestViewHostMsg_CreateMimeHandlerViewGuest::ID: - case GuestViewHostMsg_ResizeGuest::ID: *thread = BrowserThread::UI; break; default: @@ -62,11 +55,6 @@ bool handled = true; IPC_BEGIN_MESSAGE_MAP(GuestViewMessageFilter, message) IPC_MESSAGE_HANDLER(GuestViewHostMsg_AttachGuest, OnAttachGuest) - IPC_MESSAGE_HANDLER(GuestViewHostMsg_CreateMimeHandlerViewGuest, - OnCreateMimeHandlerViewGuest) - IPC_MESSAGE_HANDLER(GuestViewHostMsg_ResizeGuest, OnResizeGuest) - IPC_MESSAGE_HANDLER(GuestViewHostMsg_CanExecuteContentScriptSync, - OnCanExecuteContentScript) IPC_MESSAGE_UNHANDLED(handled = false) IPC_END_MESSAGE_MAP() return handled; @@ -90,102 +78,4 @@ params); } -void GuestViewMessageFilter::OnCreateMimeHandlerViewGuest( - int render_frame_id, - const std::string& view_id, - int element_instance_id, - const gfx::Size& element_size) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - // Since we are creating a new guest, we will create a GuestViewManager - // if we don't already have one. - auto manager = GuestViewManager::FromBrowserContext(browser_context_); - DCHECK(manager); - - auto rfh = RenderFrameHost::FromID(render_process_id_, render_frame_id); - auto embedder_web_contents = WebContents::FromRenderFrameHost(rfh); - if (!embedder_web_contents) - return; - - GuestViewManager::WebContentsCreatedCallback callback = - base::Bind(&GuestViewMessageFilter::MimeHandlerViewGuestCreatedCallback, - this, - element_instance_id, - render_process_id_, - render_frame_id, - element_size); - - base::DictionaryValue create_params; - create_params.SetString(mime_handler_view::kViewId, view_id); - create_params.SetInteger(guestview::kElementWidth, element_size.width()); - create_params.SetInteger(guestview::kElementHeight, element_size.height()); - manager->CreateGuest(MimeHandlerViewGuest::Type, - embedder_web_contents, - create_params, - callback); -} - -void GuestViewMessageFilter::OnResizeGuest(int render_frame_id, - int element_instance_id, - const gfx::Size& new_size) { - auto manager = - GuestViewManager::FromBrowserContextIfAvailable(browser_context_); - // We should have a GuestViewManager at this point. If we don't then the - // embedder is misbehaving. - if (!manager) - return; - - auto guest_web_contents = - manager->GetGuestByInstanceID(render_process_id_, element_instance_id); - auto mhvg = MimeHandlerViewGuest::FromWebContents(guest_web_contents); - if (!mhvg) - return; - - SetSizeParams set_size_params; - set_size_params.enable_auto_size.reset(new bool(false)); - set_size_params.normal_size.reset(new gfx::Size(new_size)); - mhvg->SetSize(set_size_params); -} - -void GuestViewMessageFilter::OnCanExecuteContentScript(int render_view_id, - int script_id, - bool* allowed) { - WebViewRendererState::WebViewInfo info; - WebViewRendererState::GetInstance()->GetInfo(render_process_id_, - render_view_id, &info); - - *allowed = - info.content_script_ids.find(script_id) != info.content_script_ids.end(); -} - -void GuestViewMessageFilter::MimeHandlerViewGuestCreatedCallback( - int element_instance_id, - int embedder_render_process_id, - int embedder_render_frame_id, - const gfx::Size& element_size, - WebContents* web_contents) { - auto guest_view = MimeHandlerViewGuest::FromWebContents(web_contents); - if (!guest_view) - return; - - int guest_instance_id = guest_view->guest_instance_id(); - auto rfh = RenderFrameHost::FromID(embedder_render_process_id, - embedder_render_frame_id); - if (!rfh) - return; - - base::DictionaryValue attach_params; - attach_params.SetInteger(guestview::kElementWidth, element_size.width()); - attach_params.SetInteger(guestview::kElementHeight, element_size.height()); - auto manager = - GuestViewManager::FromBrowserContextIfAvailable(browser_context_); - CHECK(manager); - manager->AttachGuest(embedder_render_process_id, - element_instance_id, - guest_instance_id, - attach_params); - - rfh->Send( - new GuestViewMsg_CreateMimeHandlerViewGuestACK(element_instance_id)); -} - } // namespace extensions
diff --git a/extensions/browser/guest_view/guest_view_message_filter.h b/extensions/browser/guest_view/guest_view_message_filter.h index 27e0d1b2..c5fb487 100644 --- a/extensions/browser/guest_view/guest_view_message_filter.h +++ b/extensions/browser/guest_view/guest_view_message_filter.h
@@ -34,8 +34,6 @@ GuestViewMessageFilter(int render_process_id, content::BrowserContext* context); - int render_process_id() const { return render_process_id_; } - private: friend class content::BrowserThread; friend class base::DeleteHelper<GuestViewMessageFilter>; @@ -52,24 +50,6 @@ void OnAttachGuest(int element_instance_id, int guest_instance_id, const base::DictionaryValue& attach_params); - void OnCreateMimeHandlerViewGuest(int render_frame_id, - const std::string& view_id, - int element_instance_id, - const gfx::Size& element_size); - void OnResizeGuest(int render_frame_id, - int element_instance_id, - const gfx::Size& new_size); - - void OnCanExecuteContentScript(int render_view_id, - int script_id, - bool* allowed); - - // Runs on UI thread. - void MimeHandlerViewGuestCreatedCallback(int element_instance_id, - int embedder_render_process_id, - int embedder_render_frame_id, - const gfx::Size& element_size, - content::WebContents* web_contents); const int render_process_id_;
diff --git a/extensions/browser/guest_view/mime_handler_view/mime_handler_view_browsertest.cc b/extensions/browser/guest_view/mime_handler_view/mime_handler_view_browsertest.cc index 5b09caf3..5d26145 100644 --- a/extensions/browser/guest_view/mime_handler_view/mime_handler_view_browsertest.cc +++ b/extensions/browser/guest_view/mime_handler_view/mime_handler_view_browsertest.cc
@@ -30,21 +30,23 @@ return extension; } - void RunTest(const std::string& path) { + void RunTestWithUrl(const GURL& url) { const extensions::Extension* extension = LoadTestExtension(); ASSERT_TRUE(extension); + extensions::ResultCatcher catcher; + ui_test_utils::NavigateToURL(browser(), url); + + if (!catcher.GetNextResult()) + FAIL() << catcher.message(); + } + + void RunTest(const std::string& path) { ASSERT_TRUE(StartEmbeddedTestServer()); embedded_test_server()->ServeFilesFromDirectory( test_data_dir_.AppendASCII("mime_handler_view")); - extensions::ResultCatcher catcher; - - ui_test_utils::NavigateToURL(browser(), - embedded_test_server()->GetURL("/" + path)); - - if (!catcher.GetNextResult()) - FAIL() << catcher.message(); + RunTestWithUrl(embedded_test_server()->GetURL("/" + path)); } }; @@ -67,3 +69,24 @@ IN_PROC_BROWSER_TEST_F(MimeHandlerViewTest, Abort) { RunTest("testAbort.csv"); } + +IN_PROC_BROWSER_TEST_F(MimeHandlerViewTest, NonAsciiHeaders) { + RunTest("testNonAsciiHeaders.csv"); +} + +IN_PROC_BROWSER_TEST_F(MimeHandlerViewTest, DataUrl) { + const char* kDataUrlCsv = "data:text/csv;base64,Y29udGVudCB0byByZWFkCg=="; + RunTestWithUrl(GURL(kDataUrlCsv)); +} + +IN_PROC_BROWSER_TEST_F(MimeHandlerViewTest, EmbeddedDataUrlObject) { + RunTest("test_embedded_data_url_object.html"); +} + +IN_PROC_BROWSER_TEST_F(MimeHandlerViewTest, EmbeddedDataUrlEmbed) { + RunTest("test_embedded_data_url_embed.html"); +} + +IN_PROC_BROWSER_TEST_F(MimeHandlerViewTest, EmbeddedDataUrlLong) { + RunTest("test_embedded_data_url_long.html"); +}
diff --git a/extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.cc b/extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.cc index a15605c0..bf91ebe4 100644 --- a/extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.cc +++ b/extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.cc
@@ -21,8 +21,8 @@ #include "extensions/browser/process_manager.h" #include "extensions/common/constants.h" #include "extensions/common/extension_messages.h" +#include "extensions/common/guest_view/extensions_guest_view_messages.h" #include "extensions/common/guest_view/guest_view_constants.h" -#include "extensions/common/guest_view/guest_view_messages.h" #include "extensions/strings/grit/extensions_strings.h" #include "ipc/ipc_message_macros.h" #include "net/base/url_util.h" @@ -246,7 +246,7 @@ void MimeHandlerViewGuest::DocumentOnLoadCompletedInMainFrame() { embedder_web_contents()->Send( - new GuestViewMsg_MimeHandlerViewGuestOnLoadCompleted( + new ExtensionsGuestViewMsg_MimeHandlerViewGuestOnLoadCompleted( element_instance_id())); }
diff --git a/extensions/browser/notification_types.h b/extensions/browser/notification_types.h index 031cb0c..f118ae4 100644 --- a/extensions/browser/notification_types.h +++ b/extensions/browser/notification_types.h
@@ -128,11 +128,6 @@ // are no details. NOTIFICATION_EXTENSION_PAGE_ACTION_COUNT_CHANGED, - // Sent when a browser action's visibility has changed. The source is the - // ExtensionPrefs* that changed, and the details are a std::string with the - // extension's ID. - NOTIFICATION_EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED, - // Sent when an extension command has been removed. The source is the // BrowserContext* and the details is an ExtensionCommandRemovedDetails // consisting of std::strings representing an extension ID, the name of the
diff --git a/extensions/browser/sandboxed_unpacker.cc b/extensions/browser/sandboxed_unpacker.cc index 29d559a..18823cb9 100644 --- a/extensions/browser/sandboxed_unpacker.cc +++ b/extensions/browser/sandboxed_unpacker.cc
@@ -322,6 +322,9 @@ } SandboxedUnpacker::~SandboxedUnpacker() { + // To avoid blocking shutdown, don't delete temporary directory here if it + // hasn't been cleaned up or passed on to another owner yet. + temp_dir_.Take(); } bool SandboxedUnpacker::OnMessageReceived(const IPC::Message& message) {
diff --git a/extensions/common/api/bluetooth_private.json b/extensions/common/api/bluetooth_private.json index 1b3bb69e..4073220 100644 --- a/extensions/common/api/bluetooth_private.json +++ b/extensions/common/api/bluetooth_private.json
@@ -56,7 +56,6 @@ { "name": "setDiscoveryFilter", "type": "function", - "platforms" : ["chromeos"], "description": "Set or clear discovery filter", "parameters": [ {
diff --git a/extensions/common/api/mime_handler.mojom b/extensions/common/api/mime_handler.mojom index 30031e63..7eefc02 100644 --- a/extensions/common/api/mime_handler.mojom +++ b/extensions/common/api/mime_handler.mojom
@@ -22,7 +22,7 @@ // The HTTP response headers of the intercepted request stored as a dictionary // mapping header name to header value. If a header name appears multiple // times, the header values are merged in the dictionary and separated by a - // ",". + // ",". Non-ASCII headers are dropped. map<string, string> response_headers; // Whether the stream is embedded within another document.
diff --git a/extensions/common/api/mime_handler_private.idl b/extensions/common/api/mime_handler_private.idl index af402d7..dfd47be 100644 --- a/extensions/common/api/mime_handler_private.idl +++ b/extensions/common/api/mime_handler_private.idl
@@ -21,7 +21,7 @@ // The HTTP response headers of the intercepted request stored as a // dictionary mapping header name to header value. If a header name appears // multiple times, the header values are merged in the dictionary and - // separated by a ", ". + // separated by a ", ". Non-ASCII headers are dropped. object responseHeaders; // Whether the stream is embedded within another document.
diff --git a/extensions/common/api/networking_private.idl b/extensions/common/api/networking_private.idl index 5200fb8..8266d4f5 100644 --- a/extensions/common/api/networking_private.idl +++ b/extensions/common/api/networking_private.idl
@@ -82,6 +82,7 @@ ActivationStateType? ActivationState; DOMString? NetworkTechnology; DOMString? RoamingState; + boolean? SIMPresent; long? SignalStrength; };
diff --git a/extensions/common/extension_message_generator.h b/extensions/common/extension_message_generator.h index b5caa8a..664514c 100644 --- a/extensions/common/extension_message_generator.h +++ b/extensions/common/extension_message_generator.h
@@ -6,4 +6,5 @@ #include "extensions/common/extension_messages.h" #include "extensions/common/extension_utility_messages.h" +#include "extensions/common/guest_view/extensions_guest_view_messages.h" #include "extensions/common/guest_view/guest_view_messages.h"
diff --git a/extensions/common/guest_view/extensions_guest_view_messages.h b/extensions/common/guest_view/extensions_guest_view_messages.h new file mode 100644 index 0000000..61da7fc --- /dev/null +++ b/extensions/common/guest_view/extensions_guest_view_messages.h
@@ -0,0 +1,47 @@ +// 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. + +// IPC messages for extensions GuestViews. +// Multiply-included message file, hence no include guard. + +#include <string> + +#include "ipc/ipc_message_macros.h" +#include "ui/gfx/geometry/size.h" +#include "ui/gfx/ipc/gfx_param_traits.h" + +#define IPC_MESSAGE_START ExtensionsGuestViewMsgStart +// Messages sent from the browser to the renderer. + +// The ACK for GuestViewHostMsg_CreateMimeHandlerViewGuest. +IPC_MESSAGE_CONTROL1(ExtensionsGuestViewMsg_CreateMimeHandlerViewGuestACK, + int /* element_instance_id */) + +// Once a MimeHandlerView guest's JavaScript onload function has been called, +// this IPC is sent to the container to notify it. +IPC_MESSAGE_CONTROL1(ExtensionsGuestViewMsg_MimeHandlerViewGuestOnLoadCompleted, + int /* element_instance_id */) + +// Messages sent from the renderer to the browser. + +// Queries whether the RenderView of the provided |routing_id| is allowed to +// inject the script with the provided |script_id|. +IPC_SYNC_MESSAGE_CONTROL2_1( + ExtensionsGuestViewHostMsg_CanExecuteContentScriptSync, + int /* routing_id */, + int /* script_id */, + bool /* allowed */) + +// Tells the browser to create a mime handler guest view for a plugin. +IPC_MESSAGE_CONTROL4(ExtensionsGuestViewHostMsg_CreateMimeHandlerViewGuest, + int /* render_frame_id */, + std::string /* view_id */, + int /* element_instance_id */, + gfx::Size /* element_size */) + +// A renderer sends this message when it wants to resize a guest. +IPC_MESSAGE_CONTROL3(ExtensionsGuestViewHostMsg_ResizeGuest, + int /* routing_id */, + int /* element_instance_id*/, + gfx::Size /* new_size */)
diff --git a/extensions/common/guest_view/guest_view_messages.h b/extensions/common/guest_view/guest_view_messages.h index b1b18ad..9772b6f 100644 --- a/extensions/common/guest_view/guest_view_messages.h +++ b/extensions/common/guest_view/guest_view_messages.h
@@ -5,26 +5,13 @@ // IPC messages for GuestViews. // Multiply-included message file, hence no include guard. -#include <string> - #include "base/values.h" #include "ipc/ipc_message_macros.h" -#include "ui/gfx/geometry/size.h" -#include "ui/gfx/ipc/gfx_param_traits.h" #define IPC_MESSAGE_START GuestViewMsgStart // Messages sent from the browser to the renderer. -// The ACK for GuestViewHostMsg_CreateMimeHandlerViewGuest. -IPC_MESSAGE_CONTROL1(GuestViewMsg_CreateMimeHandlerViewGuestACK, - int /* element_instance_id */) - -// Once a MimeHandlerView guest's JavaScript onload function has been called, -// this IPC is sent to the container to notify it. -IPC_MESSAGE_CONTROL1(GuestViewMsg_MimeHandlerViewGuestOnLoadCompleted, - int /* element_instance_id */) - // Once a RenderView proxy has been created for the guest in the embedder render // process, this IPC informs the embedder of the proxy's routing ID. IPC_MESSAGE_CONTROL2(GuestViewMsg_GuestAttached, @@ -46,21 +33,3 @@ int /* element_instance_id */, int /* guest_instance_id */, base::DictionaryValue /* attach_params */) - -// Tells the browser to create a mime handler guest view for a plugin. -IPC_MESSAGE_CONTROL4(GuestViewHostMsg_CreateMimeHandlerViewGuest, - int /* render_frame_id */, - std::string /* view_id */, - int /* element_instance_id */, - gfx::Size /* element_size */) - -// A renderer sends this message when it wants to resize a guest. -IPC_MESSAGE_CONTROL3(GuestViewHostMsg_ResizeGuest, - int /* routing_id */, - int /* element_instance_id*/, - gfx::Size /* new_size */) - -IPC_SYNC_MESSAGE_CONTROL2_1(GuestViewHostMsg_CanExecuteContentScriptSync, - int /* routing_id */, - int /* script_id */, - bool /* allowed */)
diff --git a/extensions/extensions.gypi b/extensions/extensions.gypi index 60484bec..79630432 100644 --- a/extensions/extensions.gypi +++ b/extensions/extensions.gypi
@@ -92,6 +92,7 @@ 'common/features/simple_feature_filter.h', 'common/file_util.cc', 'common/file_util.h', + 'common/guest_view/extensions_guest_view_messages.h', 'common/guest_view/guest_view_constants.cc', 'common/guest_view/guest_view_constants.h', 'common/guest_view/guest_view_messages.h', @@ -636,6 +637,8 @@ 'browser/guest_view/extension_view/extension_view_guest.h', 'browser/guest_view/extension_view/extension_view_guest_delegate.cc', 'browser/guest_view/extension_view/extension_view_guest_delegate.h', + 'browser/guest_view/extensions_guest_view_message_filter.cc', + 'browser/guest_view/extensions_guest_view_message_filter.h', 'browser/guest_view/guest_view.h', 'browser/guest_view/guest_view_base.cc', 'browser/guest_view/guest_view_base.h',
diff --git a/extensions/renderer/guest_view/guest_view_container.cc b/extensions/renderer/guest_view/guest_view_container.cc index 90e94c8..787e439 100644 --- a/extensions/renderer/guest_view/guest_view_container.cc +++ b/extensions/renderer/guest_view/guest_view_container.cc
@@ -7,6 +7,7 @@ #include "content/public/renderer/render_frame.h" #include "content/public/renderer/render_frame_observer.h" #include "content/public/renderer/render_view.h" +#include "extensions/common/guest_view/extensions_guest_view_messages.h" #include "extensions/common/guest_view/guest_view_constants.h" #include "extensions/common/guest_view/guest_view_messages.h" @@ -49,10 +50,10 @@ // static. bool GuestViewContainer::HandlesMessage(const IPC::Message& msg) { switch (msg.type()) { - case GuestViewMsg_CreateMimeHandlerViewGuestACK::ID: + case ExtensionsGuestViewMsg_CreateMimeHandlerViewGuestACK::ID: + case ExtensionsGuestViewMsg_MimeHandlerViewGuestOnLoadCompleted::ID: case GuestViewMsg_GuestAttached::ID: case GuestViewMsg_GuestDetached::ID: - case GuestViewMsg_MimeHandlerViewGuestOnLoadCompleted::ID: return true; default: return false;
diff --git a/extensions/renderer/guest_view/mime_handler_view/mime_handler_view_container.cc b/extensions/renderer/guest_view/mime_handler_view/mime_handler_view_container.cc index 7688771..d71d6a2 100644 --- a/extensions/renderer/guest_view/mime_handler_view/mime_handler_view_container.cc +++ b/extensions/renderer/guest_view/mime_handler_view/mime_handler_view_container.cc
@@ -12,6 +12,7 @@ #include "content/public/renderer/render_view.h" #include "extensions/browser/guest_view/mime_handler_view/mime_handler_view_constants.h" #include "extensions/common/extension_messages.h" +#include "extensions/common/guest_view/extensions_guest_view_messages.h" #include "extensions/common/guest_view/guest_view_constants.h" #include "extensions/common/guest_view/guest_view_messages.h" #include "gin/arguments.h" @@ -164,11 +165,12 @@ bool MimeHandlerViewContainer::OnMessageReceived(const IPC::Message& message) { bool handled = true; IPC_BEGIN_MESSAGE_MAP(MimeHandlerViewContainer, message) - IPC_MESSAGE_HANDLER(GuestViewMsg_CreateMimeHandlerViewGuestACK, + IPC_MESSAGE_HANDLER(ExtensionsGuestViewMsg_CreateMimeHandlerViewGuestACK, OnCreateMimeHandlerViewGuestACK) + IPC_MESSAGE_HANDLER( + ExtensionsGuestViewMsg_MimeHandlerViewGuestOnLoadCompleted, + OnMimeHandlerViewGuestOnLoadCompleted) IPC_MESSAGE_HANDLER(GuestViewMsg_GuestAttached, OnGuestAttached) - IPC_MESSAGE_HANDLER(GuestViewMsg_MimeHandlerViewGuestOnLoadCompleted, - OnMimeHandlerViewGuestOnLoadCompleted) IPC_MESSAGE_UNHANDLED(handled = false) IPC_END_MESSAGE_MAP() return handled; @@ -177,7 +179,7 @@ void MimeHandlerViewContainer::DidResizeElement(const gfx::Size& old_size, const gfx::Size& new_size) { element_size_ = new_size; - render_frame()->Send(new GuestViewHostMsg_ResizeGuest( + render_frame()->Send(new ExtensionsGuestViewHostMsg_ResizeGuest( render_frame()->GetRoutingID(), element_instance_id(), new_size)); } @@ -233,11 +235,7 @@ if (!window_object.Get(std::string(kPostMessageName), &post_message)) return; -#ifdef WEB_FRAME_USES_V8_LOCAL - v8::Local<v8::Value> args[] = { -#else v8::Handle<v8::Value> args[] = { -#endif message, // Post the message to any domain inside the browser plugin. The embedder // should already know what is embedded. @@ -315,9 +313,10 @@ if (!render_frame()) return; - render_frame()->Send(new GuestViewHostMsg_CreateMimeHandlerViewGuest( - render_frame()->GetRoutingID(), view_id_, element_instance_id(), - element_size_)); + render_frame()->Send( + new ExtensionsGuestViewHostMsg_CreateMimeHandlerViewGuest( + render_frame()->GetRoutingID(), view_id_, element_instance_id(), + element_size_)); } } // namespace extensions
diff --git a/extensions/renderer/messaging_bindings.cc b/extensions/renderer/messaging_bindings.cc index dab2113..1266e5b 100644 --- a/extensions/renderer/messaging_bindings.cc +++ b/extensions/renderer/messaging_bindings.cc
@@ -186,15 +186,21 @@ v8::Handle<v8::Function> callback, v8::Isolate* isolate) { GCCallback* cb = new GCCallback(object, callback, isolate); - cb->object_.SetWeak(cb, NearDeathCallback); + cb->object_.SetWeak(cb, FirstWeakCallback, + v8::WeakCallbackType::kParameter); } private: - static void NearDeathCallback( - const v8::WeakCallbackData<v8::Object, GCCallback>& data) { + static void FirstWeakCallback( + const v8::WeakCallbackInfo<GCCallback>& data) { // v8 says we need to explicitly reset weak handles from their callbacks. // It's not implicit as one might expect. data.GetParameter()->object_.Reset(); + data.SetSecondPassCallback(SecondWeakCallback); + } + + static void SecondWeakCallback( + const v8::WeakCallbackInfo<GCCallback>& data) { base::MessageLoop::current()->PostTask( FROM_HERE, base::Bind(&GCCallback::RunCallback,
diff --git a/extensions/renderer/script_context.cc b/extensions/renderer/script_context.cc index 8104fa0..9ed4c23f 100644 --- a/extensions/renderer/script_context.cc +++ b/extensions/renderer/script_context.cc
@@ -188,19 +188,12 @@ v8::Local<v8::Primitive>(v8::Undefined(isolate()))); } -#ifdef WEB_FRAME_USES_V8_LOCAL - v8::Local<v8::Value>* call_args = - reinterpret_cast<v8::Local<v8::Value>*>(argv); -#else - v8::Handle<v8::Value>* call_args = argv; -#endif - v8::Handle<v8::Object> global = v8_context()->Global(); if (!web_frame_) return handle_scope.Escape(function->Call(global, argc, argv)); return handle_scope.Escape( v8::Local<v8::Value>(web_frame_->callFunctionEvenIfScriptDisabled( - function, global, argc, call_args))); + function, global, argc, argv))); } Feature::Availability ScriptContext::GetAvailability(
diff --git a/extensions/renderer/user_script_injector.cc b/extensions/renderer/user_script_injector.cc index 08afb13..9a5a26d9 100644 --- a/extensions/renderer/user_script_injector.cc +++ b/extensions/renderer/user_script_injector.cc
@@ -11,7 +11,7 @@ #include "content/public/renderer/render_thread.h" #include "content/public/renderer/render_view.h" #include "extensions/common/extension.h" -#include "extensions/common/guest_view/guest_view_messages.h" +#include "extensions/common/guest_view/extensions_guest_view_messages.h" #include "extensions/common/permissions/permissions_data.h" #include "extensions/renderer/injection_host.h" #include "extensions/renderer/script_context.h" @@ -184,7 +184,7 @@ // webviews, and then only once per host. // TODO(hanxi): Find a more efficient way to do this. content::RenderThread::Get()->Send( - new GuestViewHostMsg_CanExecuteContentScriptSync( + new ExtensionsGuestViewHostMsg_CanExecuteContentScriptSync( routing_id, script_->id(), &allowed)); map.insert(std::pair<RoutingInfoKey, bool>(key, allowed)); }
diff --git a/extensions/shell/BUILD.gn b/extensions/shell/BUILD.gn index 64accfc..b7bf9ce 100644 --- a/extensions/shell/BUILD.gn +++ b/extensions/shell/BUILD.gn
@@ -31,6 +31,7 @@ ":version_header", "//base", "//base:prefs", + "//components/devtools_discovery", "//components/devtools_http_handler", "//components/pref_registry", "//components/update_client",
diff --git a/extensions/shell/app_shell.gyp b/extensions/shell/app_shell.gyp index 466637d..25bd151 100644 --- a/extensions/shell/app_shell.gyp +++ b/extensions/shell/app_shell.gyp
@@ -22,6 +22,7 @@ 'app_shell_version_header', '<(DEPTH)/base/base.gyp:base', '<(DEPTH)/base/base.gyp:base_prefs', + '<(DEPTH)/components/components.gyp:devtools_discovery', '<(DEPTH)/components/components.gyp:devtools_http_handler', '<(DEPTH)/components/components.gyp:pref_registry', '<(DEPTH)/components/components.gyp:update_client',
diff --git a/extensions/shell/browser/DEPS b/extensions/shell/browser/DEPS index 43dda94..03437fd5 100644 --- a/extensions/shell/browser/DEPS +++ b/extensions/shell/browser/DEPS
@@ -1,5 +1,6 @@ include_rules = [ "+chromeos", + "+components/devtools_discovery", "+components/devtools_http_handler", "+components/keyed_service", "+components/nacl/browser",
diff --git a/gin/array_buffer.cc b/gin/array_buffer.cc index d592b27..dba7763 100644 --- a/gin/array_buffer.cc +++ b/gin/array_buffer.cc
@@ -73,10 +73,10 @@ Private(v8::Isolate* isolate, v8::Handle<v8::ArrayBuffer> array); ~Private(); - static void WeakCallback( - const v8::WeakCallbackData<v8::ArrayBuffer, Private>& data); + static void FirstWeakCallback(const v8::WeakCallbackInfo<Private>& data); + static void SecondWeakCallback(const v8::WeakCallbackInfo<Private>& data); - v8::Persistent<v8::ArrayBuffer> array_buffer_; + v8::Global<v8::ArrayBuffer> array_buffer_; scoped_refptr<Private> self_reference_; v8::Isolate* isolate_; void* buffer_; @@ -108,18 +108,25 @@ &g_array_buffer_wrapper_info); array->SetAlignedPointerInInternalField(kEncodedValueIndex, this); - self_reference_ = this; // Cleared in WeakCallback. - array_buffer_.SetWeak(this, WeakCallback); + self_reference_ = this; // Cleared in SecondWeakCallback. + array_buffer_.SetWeak(this, FirstWeakCallback, + v8::WeakCallbackType::kParameter); } ArrayBuffer::Private::~Private() { PerIsolateData::From(isolate_)->allocator()->Free(buffer_, length_); } -void ArrayBuffer::Private::WeakCallback( - const v8::WeakCallbackData<v8::ArrayBuffer, Private>& data) { +void ArrayBuffer::Private::FirstWeakCallback( + const v8::WeakCallbackInfo<Private>& data) { Private* parameter = data.GetParameter(); parameter->array_buffer_.Reset(); + data.SetSecondPassCallback(SecondWeakCallback); +} + +void ArrayBuffer::Private::SecondWeakCallback( + const v8::WeakCallbackInfo<Private>& data) { + Private* parameter = data.GetParameter(); parameter->self_reference_ = NULL; }
diff --git a/gin/function_template.cc b/gin/function_template.cc index f76a05be..7b85170 100644 --- a/gin/function_template.cc +++ b/gin/function_template.cc
@@ -10,7 +10,8 @@ CallbackHolderBase::CallbackHolderBase(v8::Isolate* isolate) : v8_ref_(isolate, v8::External::New(isolate, this)) { - v8_ref_.SetWeak(this, &CallbackHolderBase::WeakCallback); + v8_ref_.SetWeak(this, &CallbackHolderBase::FirstWeakCallback, + v8::WeakCallbackType::kParameter); } CallbackHolderBase::~CallbackHolderBase() { @@ -22,9 +23,15 @@ } // static -void CallbackHolderBase::WeakCallback( - const v8::WeakCallbackData<v8::External, CallbackHolderBase>& data) { +void CallbackHolderBase::FirstWeakCallback( + const v8::WeakCallbackInfo<CallbackHolderBase>& data) { data.GetParameter()->v8_ref_.Reset(); + data.SetSecondPassCallback(SecondWeakCallback); +} + +// static +void CallbackHolderBase::SecondWeakCallback( + const v8::WeakCallbackInfo<CallbackHolderBase>& data) { delete data.GetParameter(); }
diff --git a/gin/function_template.h b/gin/function_template.h index a2e1755..1c8e2be 100644 --- a/gin/function_template.h +++ b/gin/function_template.h
@@ -51,10 +51,12 @@ virtual ~CallbackHolderBase(); private: - static void WeakCallback( - const v8::WeakCallbackData<v8::External, CallbackHolderBase>& data); + static void FirstWeakCallback( + const v8::WeakCallbackInfo<CallbackHolderBase>& data); + static void SecondWeakCallback( + const v8::WeakCallbackInfo<CallbackHolderBase>& data); - v8::Persistent<v8::External> v8_ref_; + v8::Global<v8::External> v8_ref_; DISALLOW_COPY_AND_ASSIGN(CallbackHolderBase); };
diff --git a/gin/v8_initializer.cc b/gin/v8_initializer.cc index 67b5f88d..a57afc9b 100644 --- a/gin/v8_initializer.cc +++ b/gin/v8_initializer.cc
@@ -136,8 +136,16 @@ return false; #if defined(V8_VERIFY_EXTERNAL_STARTUP_DATA) - return VerifyV8SnapshotFile(g_mapped_natives, g_natives_fingerprint) && - VerifyV8SnapshotFile(g_mapped_snapshot, g_snapshot_fingerprint); + // TODO(oth) Remove these temporary CHECKs once http://crbug.com/479537 is + // fixed. These are just here to identify whether canary failures are + // due to verification or file/vm failures. + bool natives_ok = + VerifyV8SnapshotFile(g_mapped_natives, g_natives_fingerprint); + CHECK(natives_ok); + bool snapshot_ok = + VerifyV8SnapshotFile(g_mapped_snapshot, g_snapshot_fingerprint); + CHECK(snapshot_ok); + return natives_ok && snapshot_ok; #else return true; #endif // V8_VERIFY_EXTERNAL_STARTUP_DATA
diff --git a/gin/wrappable.cc b/gin/wrappable.cc index a330fef..4137609 100644 --- a/gin/wrappable.cc +++ b/gin/wrappable.cc
@@ -22,10 +22,16 @@ return ObjectTemplateBuilder(isolate); } -void WrappableBase::WeakCallback( - const v8::WeakCallbackData<v8::Object, WrappableBase>& data) { +void WrappableBase::FirstWeakCallback( + const v8::WeakCallbackInfo<WrappableBase>& data) { WrappableBase* wrappable = data.GetParameter(); wrappable->wrapper_.Reset(); + data.SetSecondPassCallback(SecondWeakCallback); +} + +void WrappableBase::SecondWeakCallback( + const v8::WeakCallbackInfo<WrappableBase>& data) { + WrappableBase* wrappable = data.GetParameter(); delete wrappable; } @@ -55,7 +61,7 @@ wrapper->SetAlignedPointerInInternalField(kWrapperInfoIndex, info); wrapper->SetAlignedPointerInInternalField(kEncodedValueIndex, this); wrapper_.Reset(isolate, wrapper); - wrapper_.SetWeak(this, WeakCallback); + wrapper_.SetWeak(this, FirstWeakCallback, v8::WeakCallbackType::kParameter); return wrapper; }
diff --git a/gin/wrappable.h b/gin/wrappable.h index ff52b19a..ea4edcf 100644 --- a/gin/wrappable.h +++ b/gin/wrappable.h
@@ -68,10 +68,12 @@ WrapperInfo* wrapper_info); private: - static void WeakCallback( - const v8::WeakCallbackData<v8::Object, WrappableBase>& data); + static void FirstWeakCallback( + const v8::WeakCallbackInfo<WrappableBase>& data); + static void SecondWeakCallback( + const v8::WeakCallbackInfo<WrappableBase>& data); - v8::Persistent<v8::Object> wrapper_; // Weak + v8::Global<v8::Object> wrapper_; // Weak DISALLOW_COPY_AND_ASSIGN(WrappableBase); };
diff --git a/google_apis/BUILD.gn b/google_apis/BUILD.gn index a595bd88..6cf7526b 100644 --- a/google_apis/BUILD.gn +++ b/google_apis/BUILD.gn
@@ -67,13 +67,14 @@ defines += [ "USE_OFFICIAL_GOOGLE_API_KEYS=1" ] } if (google_api_key != "") { - defines += [ "GOOGLE_API_KEY=$google_api_key" ] + defines += [ "GOOGLE_API_KEY=\"$google_api_key\"" ] } if (google_default_client_id != "") { - defines += [ "GOOGLE_DEFAULT_CLIENT_ID=$google_default_client_id" ] + defines += [ "GOOGLE_DEFAULT_CLIENT_ID=\"$google_default_client_id\"" ] } if (google_default_client_secret != "") { - defines += [ "GOOGLE_DEFAULT_CLIENT_SECRET=$google_default_client_secret" ] + defines += + [ "GOOGLE_DEFAULT_CLIENT_SECRET=\"$google_default_client_secret\"" ] } } @@ -102,10 +103,10 @@ "gaia/oauth2_access_token_consumer.h", "gaia/oauth2_access_token_fetcher.cc", "gaia/oauth2_access_token_fetcher.h", - "gaia/oauth2_access_token_fetcher_impl.cc", - "gaia/oauth2_access_token_fetcher_impl.h", "gaia/oauth2_access_token_fetcher_immediate_error.cc", "gaia/oauth2_access_token_fetcher_immediate_error.h", + "gaia/oauth2_access_token_fetcher_impl.cc", + "gaia/oauth2_access_token_fetcher_impl.h", "gaia/oauth2_api_call_flow.cc", "gaia/oauth2_api_call_flow.h", "gaia/oauth2_mint_token_flow.cc",
diff --git a/google_apis/gcm/tools/mcs_probe.cc b/google_apis/gcm/tools/mcs_probe.cc index 69aac650..0f464c5 100644 --- a/google_apis/gcm/tools/mcs_probe.cc +++ b/google_apis/gcm/tools/mcs_probe.cc
@@ -359,7 +359,8 @@ } if (log_file.get()) { logger_.reset(new net::WriteToFileNetLogObserver()); - logger_->set_log_level(net::NetLog::LOG_ALL_BUT_BYTES); + logger_->set_capture_mode( + net::NetLogCaptureMode::IncludeCookiesAndCredentials()); logger_->StartObserving(&net_log_, log_file.Pass(), nullptr, nullptr); }
diff --git a/gpu/command_buffer/command_buffer_nacl.gyp b/gpu/command_buffer/command_buffer_nacl.gyp index 319fc67..e0589996 100644 --- a/gpu/command_buffer/command_buffer_nacl.gyp +++ b/gpu/command_buffer/command_buffer_nacl.gyp
@@ -27,7 +27,6 @@ 'build_nonsfi_helper': 1, }, 'dependencies': [ - '../../native_client/tools.gyp:prep_toolchain', '../../base/base_nacl.gyp:base_nacl', '../../base/base_nacl.gyp:base_nacl_nonsfi', '../../third_party/khronos/khronos.gyp:khronos_headers',
diff --git a/gpu/gpu_nacl.gyp b/gpu/gpu_nacl.gyp index 003f0af..1f38619 100644 --- a/gpu/gpu_nacl.gyp +++ b/gpu/gpu_nacl.gyp
@@ -36,7 +36,6 @@ 'dependencies': [ '../base/base_nacl.gyp:base_nacl', '../base/base_nacl.gyp:base_nacl_nonsfi', - '../native_client/tools.gyp:prep_toolchain', '../third_party/khronos/khronos.gyp:khronos_headers', 'command_buffer/command_buffer_nacl.gyp:gles2_utils_nacl', 'gles2_cmd_helper_nacl', @@ -60,7 +59,6 @@ 'dependencies': [ '../base/base_nacl.gyp:base_nacl', '../base/base_nacl.gyp:base_nacl_nonsfi', - '../native_client/tools.gyp:prep_toolchain', 'command_buffer/command_buffer_nacl.gyp:gles2_utils_nacl', ], }, @@ -82,7 +80,6 @@ 'dependencies': [ '../base/base_nacl.gyp:base_nacl', '../base/base_nacl.gyp:base_nacl_nonsfi', - '../native_client/tools.gyp:prep_toolchain', 'command_buffer_client_nacl', ], }, @@ -104,7 +101,6 @@ 'dependencies': [ '../base/base_nacl.gyp:base_nacl', '../base/base_nacl.gyp:base_nacl_nonsfi', - '../native_client/tools.gyp:prep_toolchain', 'command_buffer_common_nacl', ], }, @@ -126,7 +122,6 @@ 'dependencies': [ '../base/base_nacl.gyp:base_nacl', '../base/base_nacl.gyp:base_nacl_nonsfi', - '../native_client/tools.gyp:prep_toolchain', 'command_buffer_common_nacl', ], },
diff --git a/ios/chrome/browser/translate/chrome_ios_translate_client.mm b/ios/chrome/browser/translate/chrome_ios_translate_client.mm index 7054005..a4d0c27 100644 --- a/ios/chrome/browser/translate/chrome_ios_translate_client.mm +++ b/ios/chrome/browser/translate/chrome_ios_translate_client.mm
@@ -17,6 +17,7 @@ #include "components/translate/core/browser/translate_step.h" #include "ios/chrome/browser/infobars/infobar.h" #include "ios/chrome/browser/infobars/infobar_controller.h" +#include "ios/chrome/browser/infobars/infobar_manager_impl.h" #include "ios/chrome/browser/pref_names.h" #import "ios/chrome/browser/translate/after_translate_infobar_controller.h" #import "ios/chrome/browser/translate/before_translate_infobar_controller.h" @@ -26,7 +27,6 @@ #include "ios/chrome/browser/translate/translate_service_ios.h" #include "ios/chrome/grit/ios_theme_resources.h" #include "ios/public/provider/chrome/browser/browser_state/chrome_browser_state.h" -#include "ios/public/provider/chrome/browser/chrome_browser_provider.h" #include "ios/web/public/browser_state.h" #include "ios/web/public/web_state/web_state.h" #include "url/gurl.h" @@ -103,7 +103,7 @@ translate::TranslateInfoBarDelegate::Create( step != translate::TRANSLATE_STEP_BEFORE_TRANSLATE, translate_manager_->GetWeakPtr(), - ios::GetChromeBrowserProvider()->GetInfoBarManager(web_state()), + InfoBarManagerImpl::FromWebState(web_state()), web_state()->GetBrowserState()->IsOffTheRecord(), step, source_language, target_language, error_type, triggered_from_menu); }
diff --git a/ios/chrome/browser/ui/side_swipe_gesture_recognizer.mm b/ios/chrome/browser/ui/side_swipe_gesture_recognizer.mm index 66cb5753..47bf38a 100644 --- a/ios/chrome/browser/ui/side_swipe_gesture_recognizer.mm +++ b/ios/chrome/browser/ui/side_swipe_gesture_recognizer.mm
@@ -11,9 +11,9 @@ namespace { // The absolute maximum swipe angle from |x = y| for a swipe to begin. -CGFloat kMaxSwipeYAngle = 65; +const CGFloat kMaxSwipeYAngle = 65; // The distance between touches for a swipe to begin. -CGFloat kMinSwipeXThreshold = 4; +const CGFloat kMinSwipeXThreshold = 4; } // namespace
diff --git a/ios/public/provider/chrome/browser/chrome_browser_provider.cc b/ios/public/provider/chrome/browser/chrome_browser_provider.cc index 72a700f..25d98fd 100644 --- a/ios/public/provider/chrome/browser/chrome_browser_provider.cc +++ b/ios/public/provider/chrome/browser/chrome_browser_provider.cc
@@ -39,11 +39,6 @@ return nullptr; } -infobars::InfoBarManager* ChromeBrowserProvider::GetInfoBarManager( - web::WebState* web_state) { - return nullptr; -} - StringProvider* ChromeBrowserProvider::GetStringProvider() { return nullptr; }
diff --git a/ios/public/provider/chrome/browser/chrome_browser_provider.h b/ios/public/provider/chrome/browser/chrome_browser_provider.h index 99a7b6e..bf7a1fe 100644 --- a/ios/public/provider/chrome/browser/chrome_browser_provider.h +++ b/ios/public/provider/chrome/browser/chrome_browser_provider.h
@@ -7,10 +7,6 @@ class PrefService; -namespace infobars { -class InfoBarManager; -} - namespace net { class URLRequestContextGetter; } @@ -54,8 +50,6 @@ // Returns an instance of an infobar view. The caller is responsible for // initializing the returned object and releasing it when appropriate. virtual InfoBarViewPlaceholder* CreateInfoBarView(); - // Gets the infobar manager associated with |web_state|. - virtual infobars::InfoBarManager* GetInfoBarManager(web::WebState* web_state); // Returns an instance of a string provider. virtual StringProvider* GetStringProvider(); // Displays the Translate settings screen.
diff --git a/ios/web/public/web_state/web_state_observer.h b/ios/web/public/web_state/web_state_observer.h index cd254f84..c24e299 100644 --- a/ios/web/public/web_state/web_state_observer.h +++ b/ios/web/public/web_state/web_state_observer.h
@@ -32,6 +32,9 @@ // Returns the web state associated with this observer. WebState* web_state() const { return web_state_; } + // This method is invoked when a load request is registered. + virtual void ProvisionalNavigationStarted(const GURL& url) {} + // This method is invoked when a new non-pending navigation item is created. // This corresponds to one NavigationManager item being created // (in the case of new navigations) or renavigated to (for back/forward
diff --git a/ios/web/public/web_state/web_state_observer_bridge.h b/ios/web/public/web_state/web_state_observer_bridge.h index bc34bd61..e61bea441 100644 --- a/ios/web/public/web_state/web_state_observer_bridge.h +++ b/ios/web/public/web_state/web_state_observer_bridge.h
@@ -18,6 +18,11 @@ // web::WebStateObserver, wrap in a web::WebStateObserverBridge. @protocol CRWWebStateObserver<NSObject> @optional + +// Invoked by WebStateObserverBridge::ProvisionalNavigationStarted. +- (void)webState:(web::WebState*)webState + didStartProvisionalNavigationForURL:(const GURL&)URL; + // Invoked by WebStateObserverBridge::NavigationItemCommitted. - (void)webState:(web::WebState*)webState didCommitNavigationWithDetails: @@ -82,6 +87,7 @@ ~WebStateObserverBridge() override; // web::WebStateObserver methods. + void ProvisionalNavigationStarted(const GURL& url) override; void NavigationItemCommitted( const LoadCommittedDetails& load_details) override; void PageLoaded(
diff --git a/ios/web/web_state/web_state_observer_bridge.mm b/ios/web/web_state/web_state_observer_bridge.mm index eef1be78..c2028fe8 100644 --- a/ios/web/web_state/web_state_observer_bridge.mm +++ b/ios/web/web_state/web_state_observer_bridge.mm
@@ -14,6 +14,13 @@ WebStateObserverBridge::~WebStateObserverBridge() { } +void WebStateObserverBridge::ProvisionalNavigationStarted(const GURL& url) { + SEL selector = @selector(webState:didStartProvisionalNavigationForURL:); + if ([observer_ respondsToSelector:selector]) { + [observer_ webState:web_state() didStartProvisionalNavigationForURL:url]; + } +} + void WebStateObserverBridge::NavigationItemCommitted( const web::LoadCommittedDetails& load_detatils) { SEL selector = @selector(webState:didCommitNavigationWithDetails:);
diff --git a/ipc/ipc_channel_win.h b/ipc/ipc_channel_win.h index 3838106..04990d4 100644 --- a/ipc/ipc_channel_win.h +++ b/ipc/ipc_channel_win.h
@@ -29,26 +29,24 @@ // Mirror methods of Channel, see ipc_channel.h for description. ChannelWin(const IPC::ChannelHandle &channel_handle, Mode mode, Listener* listener); - ~ChannelWin(); + ~ChannelWin() override; // Channel implementation - virtual bool Connect() override; - virtual void Close() override; - virtual bool Send(Message* message) override; - virtual base::ProcessId GetPeerPID() const override; - virtual base::ProcessId GetSelfPID() const override; + bool Connect() override; + void Close() override; + bool Send(Message* message) override; + base::ProcessId GetPeerPID() const override; + base::ProcessId GetSelfPID() const override; static bool IsNamedServerInitialized(const std::string& channel_id); private: // ChannelReader implementation. - virtual ReadState ReadData(char* buffer, - int buffer_len, - int* bytes_read) override; - virtual bool WillDispatchInputMessage(Message* msg) override; + ReadState ReadData(char* buffer, int buffer_len, int* bytes_read) override; + bool WillDispatchInputMessage(Message* msg) override; bool DidEmptyInputBuffers() override; - virtual void HandleInternalMessage(const Message& msg) override; + void HandleInternalMessage(const Message& msg) override; static const base::string16 PipeName(const std::string& channel_id, int32* secret); @@ -59,9 +57,9 @@ DWORD bytes_written); // MessageLoop::IOHandler implementation. - virtual void OnIOCompleted(base::MessageLoopForIO::IOContext* context, - DWORD bytes_transfered, - DWORD error) override; + void OnIOCompleted(base::MessageLoopForIO::IOContext* context, + DWORD bytes_transfered, + DWORD error) override; private: struct State {
diff --git a/ipc/ipc_message_start.h b/ipc/ipc_message_start.h index 1cec310f..3cf58f921 100644 --- a/ipc/ipc_message_start.h +++ b/ipc/ipc_message_start.h
@@ -115,6 +115,7 @@ NavigatorConnectMsgStart, CastMediaMsgStart, AwMessagePortMsgStart, + ExtensionsGuestViewMsgStart, GuestViewMsgStart, // Note: CastCryptoMsgStart and CastChannelMsgStart reserved for Chromecast // internal code. Contact gunsch@ before changing/removing.
diff --git a/ipc/ipc_nacl.gyp b/ipc/ipc_nacl.gyp index 6f0d522d..5faf9af 100644 --- a/ipc/ipc_nacl.gyp +++ b/ipc/ipc_nacl.gyp
@@ -26,7 +26,6 @@ }, 'dependencies': [ '../base/base_nacl.gyp:base_nacl', - '../native_client/tools.gyp:prep_toolchain', ], }, { @@ -56,7 +55,6 @@ ], 'dependencies': [ '../base/base_nacl.gyp:base_nacl_nonsfi', - '../native_client/tools.gyp:prep_toolchain', ], }, ],
diff --git a/ipc/ipc_sync_channel_unittest.cc b/ipc/ipc_sync_channel_unittest.cc index f834ec3..468a18f 100644 --- a/ipc/ipc_sync_channel_unittest.cc +++ b/ipc/ipc_sync_channel_unittest.cc
@@ -124,7 +124,7 @@ protected: SyncChannel* channel() { return channel_.get(); } - // Functions for dervied classes to implement if they wish. + // Functions for derived classes to implement if they wish. virtual void Run() { } virtual void OnAnswer(int* answer) { NOTREACHED(); } virtual void OnAnswerDelay(Message* reply_msg) {
diff --git a/jingle/jingle_nacl.gyp b/jingle/jingle_nacl.gyp index d622fbe..b20f6414 100644 --- a/jingle/jingle_nacl.gyp +++ b/jingle/jingle_nacl.gyp
@@ -31,7 +31,6 @@ ], 'dependencies': [ '../base/base_nacl.gyp:base_nacl', - '../native_client/tools.gyp:prep_toolchain', '../net/net_nacl.gyp:net_nacl', '../third_party/libjingle/libjingle_nacl.gyp:libjingle_nacl', ],
diff --git a/mandoline/app/BUILD.gn b/mandoline/app/BUILD.gn index cc9a9318..0058c1b7 100644 --- a/mandoline/app/BUILD.gn +++ b/mandoline/app/BUILD.gn
@@ -17,7 +17,6 @@ "//build/config/sanitizers:deps", "//mojo/common", "//mojo/environment:chromium", - "//mojo/shell:init", "//mojo/shell:lib", ]
diff --git a/media/audio/audio_input_volume_unittest.cc b/media/audio/audio_input_volume_unittest.cc index d6ee313..19c712a 100644 --- a/media/audio/audio_input_volume_unittest.cc +++ b/media/audio/audio_input_volume_unittest.cc
@@ -38,13 +38,7 @@ class AudioInputVolumeTest : public ::testing::Test { protected: - AudioInputVolumeTest() - : audio_manager_(AudioManager::CreateForTesting()) -#if defined(OS_WIN) - , com_init_(base::win::ScopedCOMInitializer::kMTA) -#endif - { - } + AudioInputVolumeTest() : audio_manager_(AudioManager::CreateForTesting()) {} bool HasCoreAudioAndInputDevices() { #if defined(OS_WIN) @@ -70,27 +64,27 @@ params, device_id); EXPECT_TRUE(NULL != ais); -#if defined(OS_LINUX) || defined(OS_OPENBSD) - // Some linux devices do not support our settings, we may fail to open - // those devices. +#if defined(OS_MACOSX) + EXPECT_TRUE(ais->Open()); +#else + // Some linux devices do not support our settings and some Windows devices + // may be "currently unavailable", we may fail to open those devices. if (!ais->Open()) { // Default device should always be able to be opened. EXPECT_TRUE(AudioManagerBase::kDefaultDeviceId != device_id); ais->Close(); ais = NULL; } -#elif defined(OS_WIN) || defined(OS_MACOSX) - EXPECT_TRUE(ais->Open()); #endif return ais; } - scoped_ptr<AudioManager> audio_manager_; - #if defined(OS_WIN) base::win::ScopedCOMInitializer com_init_; #endif + + scoped_ptr<AudioManager> audio_manager_; }; #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
diff --git a/media/audio/audio_manager.cc b/media/audio/audio_manager.cc index 6ee61d16..2bbffa7 100644 --- a/media/audio/audio_manager.cc +++ b/media/audio/audio_manager.cc
@@ -11,6 +11,7 @@ #include "base/logging.h" #include "base/message_loop/message_loop.h" #include "base/power_monitor/power_monitor.h" +#include "base/synchronization/waitable_event.h" #include "build/build_config.h" #include "media/audio/fake_audio_log_factory.h" @@ -157,7 +158,19 @@ // static AudioManager* AudioManager::CreateForTesting() { - return Create(g_helper.Pointer()->fake_log_factory()); + AudioManager* manager = Create(g_helper.Pointer()->fake_log_factory()); + + // When created for testing, always ensure all methods are ready to run (even + // if they end up called from other threads. + if (!manager->GetTaskRunner()->BelongsToCurrentThread()) { + base::WaitableEvent event(false, false); + manager->GetTaskRunner()->PostTask( + FROM_HERE, + base::Bind(&base::WaitableEvent::Signal, base::Unretained(&event))); + event.Wait(); + } + + return manager; } // static
diff --git a/media/audio/audio_manager_base.cc b/media/audio/audio_manager_base.cc index edaabf4c..24e0883 100644 --- a/media/audio/audio_manager_base.cc +++ b/media/audio/audio_manager_base.cc
@@ -83,7 +83,10 @@ audio_thread_("AudioThread"), audio_log_factory_(audio_log_factory) { #if defined(OS_WIN) - audio_thread_.init_com_with_mta(true); + // Do not use MTA mode initalization as it causes hangs for a significant + // population of users and is not supported on Windows 8 for audio capture + // and rendering. See http://crbug.com/422522. + audio_thread_.init_com_with_mta(false); #elif defined(OS_MACOSX) // CoreAudio calls must occur on the main thread of the process, which in our // case is sadly the browser UI thread. Failure to execute calls on the right
diff --git a/media/audio/audio_manager_unittest.cc b/media/audio/audio_manager_unittest.cc index 26c110e..e876dd1 100644 --- a/media/audio/audio_manager_unittest.cc +++ b/media/audio/audio_manager_unittest.cc
@@ -33,19 +33,7 @@ // Windows. class AudioManagerTest : public ::testing::Test { protected: - AudioManagerTest() - : audio_manager_(AudioManager::CreateForTesting()) -#if defined(OS_WIN) - , com_init_(base::win::ScopedCOMInitializer::kMTA) -#endif - { - // Wait for audio thread initialization to complete. Otherwise the - // enumeration type may not have been set yet. - base::WaitableEvent event(false, false); - audio_manager_->GetTaskRunner()->PostTask(FROM_HERE, base::Bind( - &base::WaitableEvent::Signal, base::Unretained(&event))); - event.Wait(); - } + AudioManagerTest() : audio_manager_(AudioManager::CreateForTesting()) {} #if defined(OS_WIN) bool SetMMDeviceEnumeration() { @@ -55,13 +43,13 @@ if (amw->enumeration_type() == AudioManagerWin::kWaveEnumeration) return false; - amw->SetEnumerationType(AudioManagerWin::kMMDeviceEnumeration); + amw->set_enumeration_type(AudioManagerWin::kMMDeviceEnumeration); return true; } void SetWaveEnumeration() { AudioManagerWin* amw = static_cast<AudioManagerWin*>(audio_manager_.get()); - amw->SetEnumerationType(AudioManagerWin::kWaveEnumeration); + amw->set_enumeration_type(AudioManagerWin::kWaveEnumeration); } std::string GetDeviceIdFromPCMWaveInAudioInputStream(
diff --git a/media/audio/win/audio_device_listener_win.h b/media/audio/win/audio_device_listener_win.h index 92777a1..9c2ac482 100644 --- a/media/audio/win/audio_device_listener_win.h +++ b/media/audio/win/audio_device_listener_win.h
@@ -36,15 +36,17 @@ friend class AudioDeviceListenerWinTest; // IMMNotificationClient implementation. - STDMETHOD_(ULONG, AddRef)(); - STDMETHOD_(ULONG, Release)(); - STDMETHOD(QueryInterface)(REFIID iid, void** object); - STDMETHOD(OnPropertyValueChanged)(LPCWSTR device_id, const PROPERTYKEY key); - STDMETHOD(OnDeviceAdded)(LPCWSTR device_id); - STDMETHOD(OnDeviceRemoved)(LPCWSTR device_id); - STDMETHOD(OnDeviceStateChanged)(LPCWSTR device_id, DWORD new_state); - STDMETHOD(OnDefaultDeviceChanged)(EDataFlow flow, ERole role, - LPCWSTR new_default_device_id); + STDMETHOD_(ULONG, AddRef)() override; + STDMETHOD_(ULONG, Release)() override; + STDMETHOD(QueryInterface)(REFIID iid, void** object) override; + STDMETHOD(OnPropertyValueChanged)(LPCWSTR device_id, + const PROPERTYKEY key) override; + STDMETHOD(OnDeviceAdded)(LPCWSTR device_id) override; + STDMETHOD(OnDeviceRemoved)(LPCWSTR device_id) override; + STDMETHOD(OnDeviceStateChanged)(LPCWSTR device_id, DWORD new_state) override; + STDMETHOD(OnDefaultDeviceChanged)(EDataFlow flow, + ERole role, + LPCWSTR new_default_device_id) override; base::Closure listener_cb_; ScopedComPtr<IMMDeviceEnumerator> device_enumerator_;
diff --git a/media/audio/win/audio_device_listener_win_unittest.cc b/media/audio/win/audio_device_listener_win_unittest.cc index 49a1359..9f9dc2b 100644 --- a/media/audio/win/audio_device_listener_win_unittest.cc +++ b/media/audio/win/audio_device_listener_win_unittest.cc
@@ -25,9 +25,7 @@ class AudioDeviceListenerWinTest : public testing::Test { public: - AudioDeviceListenerWinTest() - : com_init_(ScopedCOMInitializer::kMTA) { - } + AudioDeviceListenerWinTest() {} virtual void SetUp() { if (!CoreAudioUtil::IsSupported())
diff --git a/media/audio/win/audio_low_latency_input_win.cc b/media/audio/win/audio_low_latency_input_win.cc index 72d1d72..674a097 100644 --- a/media/audio/win/audio_low_latency_input_win.cc +++ b/media/audio/win/audio_low_latency_input_win.cc
@@ -158,6 +158,11 @@ // using SetAutomaticGainControl(). StartAgc(); + if (!MarshalComPointers()) { + HandleError(S_FALSE); + return; + } + // Create and start the thread that will drive the capturing by waiting for // capture events. capture_thread_ = @@ -172,6 +177,8 @@ hr = audio_render_client_for_loopback_->Start(); started_ = SUCCEEDED(hr); + if (!started_) + HandleError(hr); } void WASAPIAudioInputStream::Stop() { @@ -350,7 +357,7 @@ } void WASAPIAudioInputStream::Run() { - ScopedCOMInitializer com_init(ScopedCOMInitializer::kMTA); + ScopedCOMInitializer com_init; // Increase the thread priority. capture_thread_->SetThreadPriority(base::ThreadPriority::REALTIME_AUDIO); @@ -369,6 +376,10 @@ LOG(WARNING) << "Failed to enable MMCSS (error code=" << err << ")."; } + // Retrieve COM pointers from the main thread. + ScopedComPtr<IAudioCaptureClient> audio_capture_client; + UnmarshalComPointers(&audio_capture_client); + // Allocate a buffer with a size that enables us to take care of cases like: // 1) The recorded buffer size is smaller, or does not match exactly with, // the selected packet size used in each callback. @@ -383,7 +394,7 @@ LARGE_INTEGER now_count; bool recording = true; bool error = false; - double volume = GetVolume(); + double volume = 0; HANDLE wait_array[2] = { stop_capture_event_.Get(), audio_samples_ready_event_.Get() }; @@ -412,11 +423,9 @@ // Retrieve the amount of data in the capture endpoint buffer, // replace it with silence if required, create callbacks for each // packet and store non-delivered data for the next event. - hr = audio_capture_client_->GetBuffer(&data_ptr, - &num_frames_to_read, - &flags, - &device_position, - &first_audio_frame_timestamp); + hr = audio_capture_client->GetBuffer( + &data_ptr, &num_frames_to_read, &flags, &device_position, + &first_audio_frame_timestamp); if (FAILED(hr)) { DLOG(ERROR) << "Failed to get data from the capture buffer"; continue; @@ -438,7 +447,7 @@ buffer_frame_index += num_frames_to_read; } - hr = audio_capture_client_->ReleaseBuffer(num_frames_to_read); + hr = audio_capture_client->ReleaseBuffer(num_frames_to_read); DLOG_IF(ERROR, FAILED(hr)) << "Failed to release capture buffer"; // Derive a delay estimate for the captured audio packet. @@ -676,8 +685,11 @@ &format_, (effects_ & AudioParameters::DUCKING) ? &kCommunicationsSessionId : NULL); - if (FAILED(hr)) + if (FAILED(hr)) { + PLOG(ERROR) << "Failed to initalize IAudioClient: " << std::hex << hr + << " : "; return hr; + } // Retrieve the length of the endpoint buffer shared between the client // and the audio engine. The buffer length determines the maximum amount @@ -768,4 +780,26 @@ return hr; } +bool WASAPIAudioInputStream::MarshalComPointers() { + DCHECK(CalledOnValidThread()); + DCHECK(!com_stream_); + HRESULT hr = CoMarshalInterThreadInterfaceInStream( + __uuidof(IAudioCaptureClient), audio_capture_client_.get(), + com_stream_.Receive()); + if (FAILED(hr)) + DLOG(ERROR) << "Marshal failed for IAudioCaptureClient: " << std::hex << hr; + DCHECK_EQ(SUCCEEDED(hr), !!com_stream_); + return SUCCEEDED(hr); +} + +void WASAPIAudioInputStream::UnmarshalComPointers( + ScopedComPtr<IAudioCaptureClient>* audio_capture_client) { + DCHECK_EQ(capture_thread_->tid(), base::PlatformThread::CurrentId()); + DCHECK(com_stream_); + HRESULT hr = CoGetInterfaceAndReleaseStream( + com_stream_.Detach(), __uuidof(IAudioCaptureClient), + audio_capture_client->ReceiveVoid()); + CHECK(SUCCEEDED(hr)); +} + } // namespace media
diff --git a/media/audio/win/audio_low_latency_input_win.h b/media/audio/win/audio_low_latency_input_win.h index e933a447..00a7de7 100644 --- a/media/audio/win/audio_low_latency_input_win.h +++ b/media/audio/win/audio_low_latency_input_win.h
@@ -92,17 +92,17 @@ // The dtor is typically called by the AudioManager only and it is usually // triggered by calling AudioInputStream::Close(). - virtual ~WASAPIAudioInputStream(); + ~WASAPIAudioInputStream() override; // Implementation of AudioInputStream. - virtual bool Open() override; - virtual void Start(AudioInputCallback* callback) override; - virtual void Stop() override; - virtual void Close() override; - virtual double GetMaxVolume() override; - virtual void SetVolume(double volume) override; - virtual double GetVolume() override; - virtual bool IsMuted() override; + bool Open() override; + void Start(AudioInputCallback* callback) override; + void Stop() override; + void Close() override; + double GetMaxVolume() override; + void SetVolume(double volume) override; + double GetVolume() override; + bool IsMuted() override; bool started() const { return started_; } @@ -111,7 +111,7 @@ private: // DelegateSimpleThread::Delegate implementation. - virtual void Run() override; + void Run() override; // Issues the OnError() callback to the |sink_|. void HandleError(HRESULT err); @@ -131,6 +131,13 @@ WAVEFORMATEX** device_format, int* effects); + // Handles sharing of COM pointers between the audio and capture threads. The + // marshal method must be called on the audio manager thread, while unmarshal + // must be called on |capture_thread_|. + bool MarshalComPointers(); + void UnmarshalComPointers( + base::win::ScopedComPtr<IAudioCaptureClient>* audio_capture_client); + // Our creator, the audio manager needs to be notified when we close. AudioManagerWin* manager_; @@ -179,8 +186,17 @@ // Pointer to the object that will receive the recorded audio samples. AudioInputCallback* sink_; - // Windows Multimedia Device (MMDevice) API interfaces. + // ------------------------------------------------------ + // Warning: COM pointers must be marshaled from the audio thread to be used + // on any other thread. Do not use any of these interfaces on a thread other + // than the audio thread. See MarshalComPointers() and UnmarshalComPointers() + // for marshaling pointers to the capture thread. + // Stream into which the COM pointers below are marshaled so that they can + // be used on another thread. + base::win::ScopedComPtr<IStream> com_stream_; + + // Windows Multimedia Device (MMDevice) API interfaces. // An IMMDevice interface which represents an audio endpoint device. base::win::ScopedComPtr<IMMDevice> endpoint_device_; @@ -198,16 +214,17 @@ // details. base::win::ScopedComPtr<IAudioClient> audio_render_client_for_loopback_; - // The IAudioCaptureClient interface enables a client to read input data - // from a capture endpoint buffer. - base::win::ScopedComPtr<IAudioCaptureClient> audio_capture_client_; - // The ISimpleAudioVolume interface enables a client to control the // master volume level of an audio session. // The volume-level is a value in the range 0.0 to 1.0. // This interface does only work with shared-mode streams. base::win::ScopedComPtr<ISimpleAudioVolume> simple_audio_volume_; + // The IAudioCaptureClient interface enables a client to read input data + // from a capture endpoint buffer. + base::win::ScopedComPtr<IAudioCaptureClient> audio_capture_client_; + // ------------------------------------------------------ + // The audio engine will signal this event each time a buffer has been // recorded. base::win::ScopedHandle audio_samples_ready_event_;
diff --git a/media/audio/win/audio_low_latency_input_win_unittest.cc b/media/audio/win/audio_low_latency_input_win_unittest.cc index 69a24f8b..60d386d 100644 --- a/media/audio/win/audio_low_latency_input_win_unittest.cc +++ b/media/audio/win/audio_low_latency_input_win_unittest.cc
@@ -163,11 +163,9 @@ class AudioInputStreamWrapper { public: explicit AudioInputStreamWrapper(AudioManager* audio_manager) - : com_init_(ScopedCOMInitializer::kMTA), - audio_man_(audio_manager), - default_params_( - audio_manager->GetInputStreamParameters( - AudioManagerBase::kDefaultDeviceId)) { + : audio_man_(audio_manager), + default_params_(audio_man_->GetInputStreamParameters( + AudioManagerBase::kDefaultDeviceId)) { EXPECT_EQ(format(), AudioParameters::AUDIO_PCM_LOW_LATENCY); frames_per_buffer_ = default_params_.frames_per_buffer(); // We expect the default buffer size to be a 10ms buffer. @@ -207,7 +205,6 @@ return ais; } - ScopedCOMInitializer com_init_; AudioManager* audio_man_; const AudioParameters default_params_; int frames_per_buffer_; @@ -254,17 +251,24 @@ DISALLOW_COPY_AND_ASSIGN(ScopedAudioInputStream); }; +class WinAudioInputTest : public testing::Test { + public: + WinAudioInputTest() : audio_manager_(AudioManager::CreateForTesting()) {} + ~WinAudioInputTest() override {} + + protected: + ScopedCOMInitializer com_init_; + scoped_ptr<AudioManager> audio_manager_; +}; + // Verify that we can retrieve the current hardware/mixing sample rate // for all available input devices. -TEST(WinAudioInputTest, WASAPIAudioInputStreamHardwareSampleRate) { - scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting()); - ABORT_AUDIO_TEST_IF_NOT(HasCoreAudioAndInputDevices(audio_manager.get())); - - ScopedCOMInitializer com_init(ScopedCOMInitializer::kMTA); +TEST_F(WinAudioInputTest, WASAPIAudioInputStreamHardwareSampleRate) { + ABORT_AUDIO_TEST_IF_NOT(HasCoreAudioAndInputDevices(audio_manager_.get())); // Retrieve a list of all available input devices. media::AudioDeviceNames device_names; - audio_manager->GetAudioInputDeviceNames(&device_names); + audio_manager_->GetAudioInputDeviceNames(&device_names); // Scan all available input devices and repeat the same test for all of them. for (media::AudioDeviceNames::const_iterator it = device_names.begin(); @@ -277,30 +281,27 @@ } // Test Create(), Close() calling sequence. -TEST(WinAudioInputTest, WASAPIAudioInputStreamCreateAndClose) { - scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting()); - ABORT_AUDIO_TEST_IF_NOT(HasCoreAudioAndInputDevices(audio_manager.get())); +TEST_F(WinAudioInputTest, WASAPIAudioInputStreamCreateAndClose) { + ABORT_AUDIO_TEST_IF_NOT(HasCoreAudioAndInputDevices(audio_manager_.get())); ScopedAudioInputStream ais( - CreateDefaultAudioInputStream(audio_manager.get())); + CreateDefaultAudioInputStream(audio_manager_.get())); ais.Close(); } // Test Open(), Close() calling sequence. -TEST(WinAudioInputTest, WASAPIAudioInputStreamOpenAndClose) { - scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting()); - ABORT_AUDIO_TEST_IF_NOT(HasCoreAudioAndInputDevices(audio_manager.get())); +TEST_F(WinAudioInputTest, WASAPIAudioInputStreamOpenAndClose) { + ABORT_AUDIO_TEST_IF_NOT(HasCoreAudioAndInputDevices(audio_manager_.get())); ScopedAudioInputStream ais( - CreateDefaultAudioInputStream(audio_manager.get())); + CreateDefaultAudioInputStream(audio_manager_.get())); EXPECT_TRUE(ais->Open()); ais.Close(); } // Test Open(), Start(), Close() calling sequence. -TEST(WinAudioInputTest, WASAPIAudioInputStreamOpenStartAndClose) { - scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting()); - ABORT_AUDIO_TEST_IF_NOT(HasCoreAudioAndInputDevices(audio_manager.get())); +TEST_F(WinAudioInputTest, WASAPIAudioInputStreamOpenStartAndClose) { + ABORT_AUDIO_TEST_IF_NOT(HasCoreAudioAndInputDevices(audio_manager_.get())); ScopedAudioInputStream ais( - CreateDefaultAudioInputStream(audio_manager.get())); + CreateDefaultAudioInputStream(audio_manager_.get())); EXPECT_TRUE(ais->Open()); MockAudioInputCallback sink; ais->Start(&sink); @@ -308,11 +309,10 @@ } // Test Open(), Start(), Stop(), Close() calling sequence. -TEST(WinAudioInputTest, WASAPIAudioInputStreamOpenStartStopAndClose) { - scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting()); - ABORT_AUDIO_TEST_IF_NOT(HasCoreAudioAndInputDevices(audio_manager.get())); +TEST_F(WinAudioInputTest, WASAPIAudioInputStreamOpenStartStopAndClose) { + ABORT_AUDIO_TEST_IF_NOT(HasCoreAudioAndInputDevices(audio_manager_.get())); ScopedAudioInputStream ais( - CreateDefaultAudioInputStream(audio_manager.get())); + CreateDefaultAudioInputStream(audio_manager_.get())); EXPECT_TRUE(ais->Open()); MockAudioInputCallback sink; ais->Start(&sink); @@ -321,11 +321,10 @@ } // Test some additional calling sequences. -TEST(WinAudioInputTest, WASAPIAudioInputStreamMiscCallingSequences) { - scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting()); - ABORT_AUDIO_TEST_IF_NOT(HasCoreAudioAndInputDevices(audio_manager.get())); +TEST_F(WinAudioInputTest, WASAPIAudioInputStreamMiscCallingSequences) { + ABORT_AUDIO_TEST_IF_NOT(HasCoreAudioAndInputDevices(audio_manager_.get())); ScopedAudioInputStream ais( - CreateDefaultAudioInputStream(audio_manager.get())); + CreateDefaultAudioInputStream(audio_manager_.get())); WASAPIAudioInputStream* wais = static_cast<WASAPIAudioInputStream*>(ais.get()); @@ -349,9 +348,8 @@ ais.Close(); } -TEST(WinAudioInputTest, WASAPIAudioInputStreamTestPacketSizes) { - scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting()); - ABORT_AUDIO_TEST_IF_NOT(HasCoreAudioAndInputDevices(audio_manager.get())); +TEST_F(WinAudioInputTest, WASAPIAudioInputStreamTestPacketSizes) { + ABORT_AUDIO_TEST_IF_NOT(HasCoreAudioAndInputDevices(audio_manager_.get())); int count = 0; base::MessageLoopForUI loop; @@ -360,7 +358,7 @@ // Create default WASAPI input stream which records in stereo using // the shared mixing rate. The default buffer size is 10ms. - AudioInputStreamWrapper aisw(audio_manager.get()); + AudioInputStreamWrapper aisw(audio_manager_.get()); ScopedAudioInputStream ais(aisw.Create()); EXPECT_TRUE(ais->Open()); @@ -419,21 +417,20 @@ } // Test that we can capture a stream in loopback. -TEST(WinAudioInputTest, WASAPIAudioInputStreamLoopback) { - scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting()); - ABORT_AUDIO_TEST_IF_NOT(audio_manager->HasAudioOutputDevices() && +TEST_F(WinAudioInputTest, WASAPIAudioInputStreamLoopback) { + ABORT_AUDIO_TEST_IF_NOT(audio_manager_->HasAudioOutputDevices() && CoreAudioUtil::IsSupported()); - AudioParameters params = audio_manager->GetInputStreamParameters( + AudioParameters params = audio_manager_->GetInputStreamParameters( AudioManagerBase::kLoopbackInputDeviceId); EXPECT_EQ(params.effects(), 0); AudioParameters output_params = - audio_manager->GetOutputStreamParameters(std::string()); + audio_manager_->GetOutputStreamParameters(std::string()); EXPECT_EQ(params.sample_rate(), output_params.sample_rate()); EXPECT_EQ(params.channel_layout(), output_params.channel_layout()); - ScopedAudioInputStream stream(audio_manager->MakeAudioInputStream( + ScopedAudioInputStream stream(audio_manager_->MakeAudioInputStream( params, AudioManagerBase::kLoopbackInputDeviceId)); ASSERT_TRUE(stream->Open()); FakeAudioInputCallback sink; @@ -453,16 +450,15 @@ // To include disabled tests in test execution, just invoke the test program // with --gtest_also_run_disabled_tests or set the GTEST_ALSO_RUN_DISABLED_TESTS // environment variable to a value greater than 0. -TEST(WinAudioInputTest, DISABLED_WASAPIAudioInputStreamRecordToFile) { - scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting()); - ABORT_AUDIO_TEST_IF_NOT(HasCoreAudioAndInputDevices(audio_manager.get())); +TEST_F(WinAudioInputTest, DISABLED_WASAPIAudioInputStreamRecordToFile) { + ABORT_AUDIO_TEST_IF_NOT(HasCoreAudioAndInputDevices(audio_manager_.get())); // Name of the output PCM file containing captured data. The output file // will be stored in the directory containing 'media_unittests.exe'. // Example of full name: \src\build\Debug\out_stereo_10sec.pcm. const char* file_name = "out_stereo_10sec.pcm"; - AudioInputStreamWrapper aisw(audio_manager.get()); + AudioInputStreamWrapper aisw(audio_manager_.get()); ScopedAudioInputStream ais(aisw.Create()); EXPECT_TRUE(ais->Open());
diff --git a/media/audio/win/audio_low_latency_output_win.cc b/media/audio/win/audio_low_latency_output_win.cc index f7b31a3..a9d637c 100644 --- a/media/audio/win/audio_low_latency_output_win.cc +++ b/media/audio/win/audio_low_latency_output_win.cc
@@ -249,6 +249,11 @@ } num_written_frames_ = endpoint_buffer_size_frames_; + if (!MarshalComPointers()) { + callback->OnError(this); + return; + } + // Create and start the thread that will drive the rendering by waiting for // render events. render_thread_.reset( @@ -333,7 +338,7 @@ } void WASAPIAudioOutputStream::Run() { - ScopedCOMInitializer com_init(ScopedCOMInitializer::kMTA); + ScopedCOMInitializer com_init; // Increase the thread priority. render_thread_->SetThreadPriority(base::ThreadPriority::REALTIME_AUDIO); @@ -352,6 +357,12 @@ LOG(WARNING) << "Failed to enable MMCSS (error code=" << err << ")."; } + // Retrieve COM pointers from the main thread. + ScopedComPtr<IAudioClient> audio_client; + ScopedComPtr<IAudioRenderClient> audio_render_client; + ScopedComPtr<IAudioClock> audio_clock; + UnmarshalComPointers(&audio_client, &audio_render_client, &audio_clock); + HRESULT hr = S_FALSE; bool playing = true; @@ -362,7 +373,7 @@ // The device frequency is the frequency generated by the hardware clock in // the audio device. The GetFrequency() method reports a constant frequency. - hr = audio_clock_->GetFrequency(&device_frequency); + hr = audio_clock->GetFrequency(&device_frequency); error = FAILED(hr); PLOG_IF(ERROR, error) << "Failed to acquire IAudioClock interface: " << std::hex << hr; @@ -383,7 +394,9 @@ break; case WAIT_OBJECT_0 + 1: // |audio_samples_render_event_| has been set. - error = !RenderAudioFromSource(device_frequency); + error = !RenderAudioFromSource(device_frequency, audio_client.get(), + audio_render_client.get(), + audio_clock.get()); break; default: error = true; @@ -391,11 +404,11 @@ } } - if (playing && error) { + if (playing && error && audio_client) { // Stop audio rendering since something has gone wrong in our main thread // loop. Note that, we are still in a "started" state, hence a Stop() call // is required to join the thread properly. - audio_client_->Stop(); + audio_client->Stop(); PLOG(ERROR) << "WASAPI rendering failed."; } @@ -405,7 +418,11 @@ } } -bool WASAPIAudioOutputStream::RenderAudioFromSource(UINT64 device_frequency) { +bool WASAPIAudioOutputStream::RenderAudioFromSource( + UINT64 device_frequency, + IAudioClient* audio_client, + IAudioRenderClient* audio_render_client, + IAudioClock* audio_clock) { TRACE_EVENT0("audio", "RenderAudioFromSource"); HRESULT hr = S_FALSE; @@ -420,7 +437,7 @@ if (share_mode_ == AUDCLNT_SHAREMODE_SHARED) { // Get the padding value which represents the amount of rendering // data that is queued up to play in the endpoint buffer. - hr = audio_client_->GetCurrentPadding(&num_queued_frames); + hr = audio_client->GetCurrentPadding(&num_queued_frames); num_available_frames = endpoint_buffer_size_frames_ - num_queued_frames; if (FAILED(hr)) { @@ -462,8 +479,7 @@ for (size_t n = 0; n < num_packets; ++n) { // Grab all available space in the rendering endpoint buffer // into which the client can write a data packet. - hr = audio_render_client_->GetBuffer(packet_size_frames_, - &audio_data); + hr = audio_render_client->GetBuffer(packet_size_frames_, &audio_data); if (FAILED(hr)) { DLOG(ERROR) << "Failed to use rendering audio buffer: " << std::hex << hr; @@ -477,7 +493,7 @@ // unit at the render side. UINT64 position = 0; uint32 audio_delay_bytes = 0; - hr = audio_clock_->GetPosition(&position, NULL); + hr = audio_clock->GetPosition(&position, NULL); if (SUCCEEDED(hr)) { // Stream position of the sample that is currently playing // through the speaker. @@ -517,7 +533,7 @@ // Render silence if we were not able to fill up the buffer totally. DWORD flags = (num_filled_bytes < packet_size_bytes_) ? AUDCLNT_BUFFERFLAGS_SILENT : 0; - audio_render_client_->ReleaseBuffer(packet_size_frames_, flags); + audio_render_client->ReleaseBuffer(packet_size_frames_, flags); num_written_frames_ += packet_size_frames_; } @@ -622,4 +638,73 @@ source_ = NULL; } +bool WASAPIAudioOutputStream::MarshalComPointers() { + DCHECK_EQ(creating_thread_id_, base::PlatformThread::CurrentId()); + DCHECK(!com_stream_); + + ScopedComPtr<IStream> com_stream; + HRESULT hr = CreateStreamOnHGlobal(NULL, TRUE, com_stream.Receive()); + if (FAILED(hr)) { + DLOG(ERROR) << "Failed to create stream for marshaling COM pointers."; + return false; + } + + hr = CoMarshalInterface(com_stream.get(), __uuidof(IAudioClient), + audio_client_.get(), MSHCTX_INPROC, NULL, + MSHLFLAGS_NORMAL); + if (FAILED(hr)) { + DLOG(ERROR) << "Marshal failed for IAudioClient: " << std::hex << hr; + return false; + } + + hr = CoMarshalInterface(com_stream.get(), __uuidof(IAudioRenderClient), + audio_render_client_.get(), MSHCTX_INPROC, NULL, + MSHLFLAGS_NORMAL); + if (FAILED(hr)) { + DLOG(ERROR) << "Marshal failed for IAudioRenderClient: " << std::hex << hr; + return false; + } + + hr = CoMarshalInterface(com_stream.get(), __uuidof(IAudioClock), + audio_clock_.get(), MSHCTX_INPROC, NULL, + MSHLFLAGS_NORMAL); + if (FAILED(hr)) { + DLOG(ERROR) << "Marshal failed for IAudioClock: " << std::hex << hr; + return false; + } + + LARGE_INTEGER pos = {0}; + hr = com_stream->Seek(pos, STREAM_SEEK_SET, NULL); + if (FAILED(hr)) { + DLOG(ERROR) << "Failed to seek IStream for marshaling: " << std::hex << hr; + return false; + } + + com_stream_ = com_stream.Pass(); + return true; +} + +void WASAPIAudioOutputStream::UnmarshalComPointers( + ScopedComPtr<IAudioClient>* audio_client, + ScopedComPtr<IAudioRenderClient>* audio_render_client, + ScopedComPtr<IAudioClock>* audio_clock) { + DCHECK_EQ(render_thread_->tid(), base::PlatformThread::CurrentId()); + + DCHECK(com_stream_); + ScopedComPtr<IStream> com_stream; + com_stream = com_stream_.Pass(); + + HRESULT hr = CoUnmarshalInterface(com_stream.get(), __uuidof(IAudioClient), + audio_client->ReceiveVoid()); + CHECK(SUCCEEDED(hr)); + + hr = CoUnmarshalInterface(com_stream.get(), __uuidof(IAudioRenderClient), + audio_render_client->ReceiveVoid()); + CHECK(SUCCEEDED(hr)); + + hr = CoUnmarshalInterface(com_stream.get(), __uuidof(IAudioClock), + audio_clock->ReceiveVoid()); + CHECK(SUCCEEDED(hr)); +} + } // namespace media
diff --git a/media/audio/win/audio_low_latency_output_win.h b/media/audio/win/audio_low_latency_output_win.h index 1584a46..30e791b 100644 --- a/media/audio/win/audio_low_latency_output_win.h +++ b/media/audio/win/audio_low_latency_output_win.h
@@ -128,15 +128,15 @@ // The dtor is typically called by the AudioManager only and it is usually // triggered by calling AudioOutputStream::Close(). - virtual ~WASAPIAudioOutputStream(); + ~WASAPIAudioOutputStream() override; // Implementation of AudioOutputStream. - virtual bool Open() override; - virtual void Start(AudioSourceCallback* callback) override; - virtual void Stop() override; - virtual void Close() override; - virtual void SetVolume(double volume) override; - virtual void GetVolume(double* volume) override; + bool Open() override; + void Start(AudioSourceCallback* callback) override; + void Stop() override; + void Close() override; + void SetVolume(double volume) override; + void GetVolume(double* volume) override; // Retrieves the sample rate the audio engine uses for its internal // processing/mixing of shared-mode streams. To fetch the settings for the @@ -151,13 +151,16 @@ private: // DelegateSimpleThread::Delegate implementation. - virtual void Run() override; + void Run() override; // Core part of the thread loop which controls the actual rendering. // Checks available amount of space in the endpoint buffer and reads // data from the client to fill up the buffer without causing audio // glitches. - bool RenderAudioFromSource(UINT64 device_frequency); + bool RenderAudioFromSource(UINT64 device_frequency, + IAudioClient* thread_audio_client, + IAudioRenderClient* thread_audio_render_client, + IAudioClock* thread_audio_clock); // Called when the device will be opened in exclusive mode and use the // application specified format. @@ -172,6 +175,15 @@ // |source_| is set to NULL. void StopThread(); + // Handles sharing of COM pointers between the audio and render threads. The + // marshal method must be called on the audio manager thread, while unmarshal + // must be called on |render_thread_|. + bool MarshalComPointers(); + void UnmarshalComPointers( + base::win::ScopedComPtr<IAudioClient>* audio_client, + base::win::ScopedComPtr<IAudioRenderClient>* audio_render_client, + base::win::ScopedComPtr<IAudioClock>* audio_clock); + // Contains the thread ID of the creating thread. base::PlatformThreadId creating_thread_id_; @@ -221,6 +233,18 @@ // Pointer to the client that will deliver audio samples to be played out. AudioSourceCallback* source_; + // ------------------------------------------------------ + // Warning: COM pointers must be marshaled from the audio thread to be used + // on any other thread. Do not use any of these interfaces on a thread other + // than the audio thread. See MarshalComPointers() and UnmarshalComPointers() + // for marshaling pointers to the render thread. + + // Stream into which the COM pointers below are marshaled so that they can + // be used on another thread. + base::win::ScopedComPtr<IStream> com_stream_; + + // Windows Audio Session API (WASAPI) interfaces. + // An IAudioClient interface which enables a client to create and initialize // an audio stream between an audio application and the audio engine. base::win::ScopedComPtr<IAudioClient> audio_client_; @@ -229,6 +253,9 @@ // data to a rendering endpoint buffer. base::win::ScopedComPtr<IAudioRenderClient> audio_render_client_; + base::win::ScopedComPtr<IAudioClock> audio_clock_; + // ------------------------------------------------------ + // The audio engine will signal this event each time a buffer becomes // ready to be filled by the client. base::win::ScopedHandle audio_samples_render_event_; @@ -239,8 +266,6 @@ // Container for retrieving data from AudioSourceCallback::OnMoreData(). scoped_ptr<AudioBus> audio_bus_; - base::win::ScopedComPtr<IAudioClock> audio_clock_; - DISALLOW_COPY_AND_ASSIGN(WASAPIAudioOutputStream); };
diff --git a/media/audio/win/audio_low_latency_output_win_unittest.cc b/media/audio/win/audio_low_latency_output_win_unittest.cc index afd565a9..193c0821 100644 --- a/media/audio/win/audio_low_latency_output_win_unittest.cc +++ b/media/audio/win/audio_low_latency_output_win_unittest.cc
@@ -6,6 +6,7 @@ #include <mmsystem.h> #include "base/basictypes.h" +#include "base/bind.h" #include "base/environment.h" #include "base/files/file_util.h" #include "base/memory/scoped_ptr.h" @@ -226,14 +227,24 @@ return aos; } +class WASAPIAudioOutputStreamTest : public testing::Test { + public: + WASAPIAudioOutputStreamTest() + : audio_manager_(AudioManager::CreateForTesting()) {} + ~WASAPIAudioOutputStreamTest() override {} + + protected: + ScopedCOMInitializer com_init_; + scoped_ptr<AudioManager> audio_manager_; +}; + // Verify that we can retrieve the current hardware/mixing sample rate // for the default audio device. // TODO(henrika): modify this test when we support full device enumeration. -TEST(WASAPIAudioOutputStreamTest, HardwareSampleRate) { +TEST_F(WASAPIAudioOutputStreamTest, HardwareSampleRate) { // Skip this test in exclusive mode since the resulting rate is only utilized // for shared mode streams. - scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting()); - ABORT_AUDIO_TEST_IF_NOT(HasCoreAudioAndOutputDevices(audio_manager.get()) && + ABORT_AUDIO_TEST_IF_NOT(HasCoreAudioAndOutputDevices(audio_manager_.get()) && ExclusiveModeIsEnabled()); // Default device intended for games, system notification sounds, @@ -244,27 +255,24 @@ } // Test Create(), Close() calling sequence. -TEST(WASAPIAudioOutputStreamTest, CreateAndClose) { - scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting()); - ABORT_AUDIO_TEST_IF_NOT(HasCoreAudioAndOutputDevices(audio_manager.get())); - AudioOutputStream* aos = CreateDefaultAudioOutputStream(audio_manager.get()); +TEST_F(WASAPIAudioOutputStreamTest, CreateAndClose) { + ABORT_AUDIO_TEST_IF_NOT(HasCoreAudioAndOutputDevices(audio_manager_.get())); + AudioOutputStream* aos = CreateDefaultAudioOutputStream(audio_manager_.get()); aos->Close(); } // Test Open(), Close() calling sequence. -TEST(WASAPIAudioOutputStreamTest, OpenAndClose) { - scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting()); - ABORT_AUDIO_TEST_IF_NOT(HasCoreAudioAndOutputDevices(audio_manager.get())); - AudioOutputStream* aos = CreateDefaultAudioOutputStream(audio_manager.get()); +TEST_F(WASAPIAudioOutputStreamTest, OpenAndClose) { + ABORT_AUDIO_TEST_IF_NOT(HasCoreAudioAndOutputDevices(audio_manager_.get())); + AudioOutputStream* aos = CreateDefaultAudioOutputStream(audio_manager_.get()); EXPECT_TRUE(aos->Open()); aos->Close(); } // Test Open(), Start(), Close() calling sequence. -TEST(WASAPIAudioOutputStreamTest, OpenStartAndClose) { - scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting()); - ABORT_AUDIO_TEST_IF_NOT(HasCoreAudioAndOutputDevices(audio_manager.get())); - AudioOutputStream* aos = CreateDefaultAudioOutputStream(audio_manager.get()); +TEST_F(WASAPIAudioOutputStreamTest, OpenStartAndClose) { + ABORT_AUDIO_TEST_IF_NOT(HasCoreAudioAndOutputDevices(audio_manager_.get())); + AudioOutputStream* aos = CreateDefaultAudioOutputStream(audio_manager_.get()); EXPECT_TRUE(aos->Open()); MockAudioSourceCallback source; EXPECT_CALL(source, OnError(aos)) @@ -274,10 +282,9 @@ } // Test Open(), Start(), Stop(), Close() calling sequence. -TEST(WASAPIAudioOutputStreamTest, OpenStartStopAndClose) { - scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting()); - ABORT_AUDIO_TEST_IF_NOT(HasCoreAudioAndOutputDevices(audio_manager.get())); - AudioOutputStream* aos = CreateDefaultAudioOutputStream(audio_manager.get()); +TEST_F(WASAPIAudioOutputStreamTest, OpenStartStopAndClose) { + ABORT_AUDIO_TEST_IF_NOT(HasCoreAudioAndOutputDevices(audio_manager_.get())); + AudioOutputStream* aos = CreateDefaultAudioOutputStream(audio_manager_.get()); EXPECT_TRUE(aos->Open()); MockAudioSourceCallback source; EXPECT_CALL(source, OnError(aos)) @@ -288,10 +295,9 @@ } // Test SetVolume(), GetVolume() -TEST(WASAPIAudioOutputStreamTest, Volume) { - scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting()); - ABORT_AUDIO_TEST_IF_NOT(HasCoreAudioAndOutputDevices(audio_manager.get())); - AudioOutputStream* aos = CreateDefaultAudioOutputStream(audio_manager.get()); +TEST_F(WASAPIAudioOutputStreamTest, Volume) { + ABORT_AUDIO_TEST_IF_NOT(HasCoreAudioAndOutputDevices(audio_manager_.get())); + AudioOutputStream* aos = CreateDefaultAudioOutputStream(audio_manager_.get()); // Initial volume should be full volume (1.0). double volume = 0.0; @@ -324,11 +330,10 @@ } // Test some additional calling sequences. -TEST(WASAPIAudioOutputStreamTest, MiscCallingSequences) { - scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting()); - ABORT_AUDIO_TEST_IF_NOT(HasCoreAudioAndOutputDevices(audio_manager.get())); +TEST_F(WASAPIAudioOutputStreamTest, MiscCallingSequences) { + ABORT_AUDIO_TEST_IF_NOT(HasCoreAudioAndOutputDevices(audio_manager_.get())); - AudioOutputStream* aos = CreateDefaultAudioOutputStream(audio_manager.get()); + AudioOutputStream* aos = CreateDefaultAudioOutputStream(audio_manager_.get()); WASAPIAudioOutputStream* waos = static_cast<WASAPIAudioOutputStream*>(aos); // Open(), Open() is a valid calling sequence (second call does nothing). @@ -363,16 +368,15 @@ } // Use preferred packet size and verify that rendering starts. -TEST(WASAPIAudioOutputStreamTest, ValidPacketSize) { - scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting()); - ABORT_AUDIO_TEST_IF_NOT(HasCoreAudioAndOutputDevices(audio_manager.get())); +TEST_F(WASAPIAudioOutputStreamTest, ValidPacketSize) { + ABORT_AUDIO_TEST_IF_NOT(HasCoreAudioAndOutputDevices(audio_manager_.get())); base::MessageLoopForUI loop; MockAudioSourceCallback source; // Create default WASAPI output stream which plays out in stereo using // the shared mixing rate. The default buffer size is 10ms. - AudioOutputStreamWrapper aosw(audio_manager.get()); + AudioOutputStreamWrapper aosw(audio_manager_.get()); AudioOutputStream* aos = aosw.Create(); EXPECT_TRUE(aos->Open()); @@ -401,11 +405,10 @@ // with --gtest_also_run_disabled_tests or set the GTEST_ALSO_RUN_DISABLED_TESTS // environment variable to a value greater than 0. // The test files are approximately 20 seconds long. -TEST(WASAPIAudioOutputStreamTest, DISABLED_ReadFromStereoFile) { - scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting()); - ABORT_AUDIO_TEST_IF_NOT(HasCoreAudioAndOutputDevices(audio_manager.get())); +TEST_F(WASAPIAudioOutputStreamTest, DISABLED_ReadFromStereoFile) { + ABORT_AUDIO_TEST_IF_NOT(HasCoreAudioAndOutputDevices(audio_manager_.get())); - AudioOutputStreamWrapper aosw(audio_manager.get()); + AudioOutputStreamWrapper aosw(audio_manager_.get()); AudioOutputStream* aos = aosw.Create(); EXPECT_TRUE(aos->Open()); @@ -450,12 +453,11 @@ // certain set of audio parameters and a sample rate of 48kHz. // The expected outcomes of each setting in this test has been derived // manually using log outputs (--v=1). -TEST(WASAPIAudioOutputStreamTest, ExclusiveModeBufferSizesAt48kHz) { - scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting()); - ABORT_AUDIO_TEST_IF_NOT(HasCoreAudioAndOutputDevices(audio_manager.get()) && +TEST_F(WASAPIAudioOutputStreamTest, ExclusiveModeBufferSizesAt48kHz) { + ABORT_AUDIO_TEST_IF_NOT(HasCoreAudioAndOutputDevices(audio_manager_.get()) && ExclusiveModeIsEnabled()); - AudioOutputStreamWrapper aosw(audio_manager.get()); + AudioOutputStreamWrapper aosw(audio_manager_.get()); // 10ms @ 48kHz shall work. // Note that, this is the same size as we can use for shared-mode streaming @@ -498,12 +500,11 @@ // certain set of audio parameters and a sample rate of 44.1kHz. // The expected outcomes of each setting in this test has been derived // manually using log outputs (--v=1). -TEST(WASAPIAudioOutputStreamTest, ExclusiveModeBufferSizesAt44kHz) { - scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting()); - ABORT_AUDIO_TEST_IF_NOT(HasCoreAudioAndOutputDevices(audio_manager.get()) && +TEST_F(WASAPIAudioOutputStreamTest, ExclusiveModeBufferSizesAt44kHz) { + ABORT_AUDIO_TEST_IF_NOT(HasCoreAudioAndOutputDevices(audio_manager_.get()) && ExclusiveModeIsEnabled()); - AudioOutputStreamWrapper aosw(audio_manager.get()); + AudioOutputStreamWrapper aosw(audio_manager_.get()); // 10ms @ 44.1kHz does not work due to misalignment. // This test will propose an aligned buffer size of 10.1587ms. @@ -553,9 +554,8 @@ // Verify that we can open and start the output stream in exclusive mode at // the lowest possible delay at 48kHz. -TEST(WASAPIAudioOutputStreamTest, ExclusiveModeMinBufferSizeAt48kHz) { - scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting()); - ABORT_AUDIO_TEST_IF_NOT(HasCoreAudioAndOutputDevices(audio_manager.get()) && +TEST_F(WASAPIAudioOutputStreamTest, ExclusiveModeMinBufferSizeAt48kHz) { + ABORT_AUDIO_TEST_IF_NOT(HasCoreAudioAndOutputDevices(audio_manager_.get()) && ExclusiveModeIsEnabled()); base::MessageLoopForUI loop; @@ -563,7 +563,7 @@ // Create exclusive-mode WASAPI output stream which plays out in stereo // using the minimum buffer size at 48kHz sample rate. - AudioOutputStreamWrapper aosw(audio_manager.get()); + AudioOutputStreamWrapper aosw(audio_manager_.get()); AudioOutputStream* aos = aosw.Create(48000, 160); EXPECT_TRUE(aos->Open()); @@ -588,16 +588,15 @@ // Verify that we can open and start the output stream in exclusive mode at // the lowest possible delay at 44.1kHz. -TEST(WASAPIAudioOutputStreamTest, ExclusiveModeMinBufferSizeAt44kHz) { +TEST_F(WASAPIAudioOutputStreamTest, ExclusiveModeMinBufferSizeAt44kHz) { ABORT_AUDIO_TEST_IF_NOT(ExclusiveModeIsEnabled()); - scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting()); base::MessageLoopForUI loop; MockAudioSourceCallback source; // Create exclusive-mode WASAPI output stream which plays out in stereo // using the minimum buffer size at 44.1kHz sample rate. - AudioOutputStreamWrapper aosw(audio_manager.get()); + AudioOutputStreamWrapper aosw(audio_manager_.get()); AudioOutputStream* aos = aosw.Create(44100, 160); EXPECT_TRUE(aos->Open());
diff --git a/media/audio/win/audio_manager_win.h b/media/audio/win/audio_manager_win.h index ce61eb6..afd1ef9b 100644 --- a/media/audio/win/audio_manager_win.h +++ b/media/audio/win/audio_manager_win.h
@@ -21,35 +21,35 @@ AudioManagerWin(AudioLogFactory* audio_log_factory); // Implementation of AudioManager. - virtual bool HasAudioOutputDevices() override; - virtual bool HasAudioInputDevices() override; - virtual base::string16 GetAudioInputDeviceModel() override; - virtual void ShowAudioInputSettings() override; - virtual void GetAudioInputDeviceNames( - AudioDeviceNames* device_names) override; - virtual void GetAudioOutputDeviceNames( - AudioDeviceNames* device_names) override; - virtual AudioParameters GetInputStreamParameters( + bool HasAudioOutputDevices() override; + bool HasAudioInputDevices() override; + base::string16 GetAudioInputDeviceModel() override; + void ShowAudioInputSettings() override; + void GetAudioInputDeviceNames(AudioDeviceNames* device_names) override; + void GetAudioOutputDeviceNames(AudioDeviceNames* device_names) override; + AudioParameters GetInputStreamParameters( const std::string& device_id) override; - virtual std::string GetAssociatedOutputDeviceID( + std::string GetAssociatedOutputDeviceID( const std::string& input_device_id) override; // Implementation of AudioManagerBase. - virtual AudioOutputStream* MakeLinearOutputStream( + AudioOutputStream* MakeLinearOutputStream( const AudioParameters& params) override; - virtual AudioOutputStream* MakeLowLatencyOutputStream( + AudioOutputStream* MakeLowLatencyOutputStream( const AudioParameters& params, const std::string& device_id) override; - virtual AudioInputStream* MakeLinearInputStream( - const AudioParameters& params, const std::string& device_id) override; - virtual AudioInputStream* MakeLowLatencyInputStream( - const AudioParameters& params, const std::string& device_id) override; - virtual std::string GetDefaultOutputDeviceID() override; + AudioInputStream* MakeLinearInputStream( + const AudioParameters& params, + const std::string& device_id) override; + AudioInputStream* MakeLowLatencyInputStream( + const AudioParameters& params, + const std::string& device_id) override; + std::string GetDefaultOutputDeviceID() override; protected: - virtual ~AudioManagerWin(); + ~AudioManagerWin() override; - virtual AudioParameters GetPreferredOutputStreamParameters( + AudioParameters GetPreferredOutputStreamParameters( const std::string& output_device_id, const AudioParameters& input_params) override; @@ -64,9 +64,7 @@ EnumerationType enumeration_type_; EnumerationType enumeration_type() { return enumeration_type_; } - void SetEnumerationType(EnumerationType type) { - enumeration_type_ = type; - } + void set_enumeration_type(EnumerationType type) { enumeration_type_ = type; } inline bool core_audio_supported() const { return enumeration_type_ == kMMDeviceEnumeration;
diff --git a/media/audio/win/audio_output_win_unittest.cc b/media/audio/win/audio_output_win_unittest.cc index 93454fc9..c1dc5b4 100644 --- a/media/audio/win/audio_output_win_unittest.cc +++ b/media/audio/win/audio_output_win_unittest.cc
@@ -427,7 +427,7 @@ ABORT_AUDIO_TEST_IF_NOT(audio_man->HasAudioOutputDevices()); // The WASAPI API requires a correct COM environment. - ScopedCOMInitializer com_init(ScopedCOMInitializer::kMTA); + ScopedCOMInitializer com_init; // Use 10 ms buffer size for WASAPI and 50 ms buffer size for Wave. // Take the existing native sample rate into account.
diff --git a/media/audio/win/core_audio_util_win.cc b/media/audio/win/core_audio_util_win.cc index 53530dfc..4f72564 100644 --- a/media/audio/win/core_audio_util_win.cc +++ b/media/audio/win/core_audio_util_win.cc
@@ -178,10 +178,12 @@ // fail with CO_E_NOTINITIALIZED in combination with certain 3rd party // modules. Calling CoInitializeEx is an attempt to resolve the reported // issues. See http://crbug.com/378465 for details. - hr = CoInitializeEx(NULL, COINIT_MULTITHREADED); + hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); if (SUCCEEDED(hr)) { - hr = device_enumerator.CreateInstance(__uuidof(MMDeviceEnumerator), - NULL, CLSCTX_INPROC_SERVER); + hr = device_enumerator.CreateInstance(__uuidof(MMDeviceEnumerator), NULL, + CLSCTX_INPROC_SERVER); + } else { + LOG(ERROR) << "CoCreateInstance still failed! " << std::hex << hr; } } return device_enumerator;
diff --git a/media/audio/win/core_audio_util_win_unittest.cc b/media/audio/win/core_audio_util_win_unittest.cc index 31f91e9..1a24e5d2 100644 --- a/media/audio/win/core_audio_util_win_unittest.cc +++ b/media/audio/win/core_audio_util_win_unittest.cc
@@ -19,13 +19,10 @@ class CoreAudioUtilWinTest : public ::testing::Test { protected: - // The test runs on a COM thread in the multithreaded apartment (MTA). + // The test runs on a COM thread in the singlethreaded apartment (STA). // If we don't initialize the COM library on a thread before using COM, // all function calls will return CO_E_NOTINITIALIZED. - CoreAudioUtilWinTest() - : com_init_(ScopedCOMInitializer::kMTA) { - DCHECK(com_init_.succeeded()); - } + CoreAudioUtilWinTest() { DCHECK(com_init_.succeeded()); } virtual ~CoreAudioUtilWinTest() {} bool DevicesAvailable() {
diff --git a/media/audio/win/device_enumeration_win.h b/media/audio/win/device_enumeration_win.h index e61a3318..e6485db0 100644 --- a/media/audio/win/device_enumeration_win.h +++ b/media/audio/win/device_enumeration_win.h
@@ -17,7 +17,7 @@ // Example record in the output list: // - device_name: "Microphone (Realtek High Definition Audio)". // - unique_id: "{0.0.1.00000000}.{8db6020f-18e3-4f25-b6f5-7726c9122574}" -// This method must be called from a COM thread using MTA. +// This method must be called from a COM thread using STA. bool GetInputDeviceNamesWin(media::AudioDeviceNames* device_names); bool GetOutputDeviceNamesWin(media::AudioDeviceNames* device_names);
diff --git a/media/audio/win/wavein_input_win.h b/media/audio/win/wavein_input_win.h index 6b98d0d..a2c77c34 100644 --- a/media/audio/win/wavein_input_win.h +++ b/media/audio/win/wavein_input_win.h
@@ -32,20 +32,20 @@ const AudioParameters& params, int num_buffers, const std::string& device_id); - virtual ~PCMWaveInAudioInputStream(); + ~PCMWaveInAudioInputStream() override; // Implementation of AudioInputStream. - virtual bool Open() override; - virtual void Start(AudioInputCallback* callback) override; - virtual void Stop() override; - virtual void Close() override; + bool Open() override; + void Start(AudioInputCallback* callback) override; + void Stop() override; + void Close() override; // TODO(henrika): Add volume support using the Audio Mixer API. - virtual double GetMaxVolume() override; - virtual void SetVolume(double volume) override; - virtual double GetVolume() override; - virtual bool SetAutomaticGainControl(bool enabled) override; - virtual bool GetAutomaticGainControl() override; - virtual bool IsMuted() override; + double GetMaxVolume() override; + void SetVolume(double volume) override; + double GetVolume() override; + bool SetAutomaticGainControl(bool enabled) override; + bool GetAutomaticGainControl() override; + bool IsMuted() override; private: enum State {
diff --git a/media/audio/win/waveout_output_win.h b/media/audio/win/waveout_output_win.h index 5c7009d..bd9da5e 100644 --- a/media/audio/win/waveout_output_win.h +++ b/media/audio/win/waveout_output_win.h
@@ -38,15 +38,15 @@ const AudioParameters& params, int num_buffers, UINT device_id); - virtual ~PCMWaveOutAudioOutputStream(); + ~PCMWaveOutAudioOutputStream() override; // Implementation of AudioOutputStream. - virtual bool Open(); - virtual void Close(); - virtual void Start(AudioSourceCallback* callback); - virtual void Stop(); - virtual void SetVolume(double volume); - virtual void GetVolume(double* volume); + bool Open() override; + void Close() override; + void Start(AudioSourceCallback* callback) override; + void Stop() override; + void SetVolume(double volume) override; + void GetVolume(double* volume) override; // Sends a buffer to the audio driver for playback. void QueueNextPacket(WAVEHDR* buffer);
diff --git a/media/base/BUILD.gn b/media/base/BUILD.gn index 861fb678..1f05d4467 100644 --- a/media/base/BUILD.gn +++ b/media/base/BUILD.gn
@@ -4,6 +4,7 @@ import("//build/config/android/config.gni") import("//build/config/arm.gni") +import("//build/config/features.gni") import("//build/config/ui.gni") import("//build/config/linux/pkg_config.gni") import("//media/media_options.gni") @@ -241,9 +242,7 @@ sources += [ "user_input_monitor_mac.cc" ] # Required by video_frame.cc. - libs = [ - "CoreVideo.framework" - ] + libs = [ "CoreVideo.framework" ] } else if (is_win) { sources += [ "user_input_monitor_win.cc" ] } else {
diff --git a/media/base/eme_constants.h b/media/base/eme_constants.h index 8bb3883..0b246709 100644 --- a/media/base/eme_constants.h +++ b/media/base/eme_constants.h
@@ -58,37 +58,30 @@ typedef uint32_t SupportedCodecs; -enum EmeSessionTypeSupport { +enum class EmeSessionTypeSupport { // Invalid default value. - EME_SESSION_TYPE_INVALID, + INVALID, // The session type is not supported. - EME_SESSION_TYPE_NOT_SUPPORTED, + NOT_SUPPORTED, // The session type is supported if a distinctive identifier is available. - EME_SESSION_TYPE_SUPPORTED_WITH_IDENTIFIER, + SUPPORTED_WITH_IDENTIFIER, // The session type is always supported. - EME_SESSION_TYPE_SUPPORTED, + SUPPORTED, }; // Used to declare support for distinctive identifier and persistent state. // These are purposefully limited to not allow one to require the other, so that // transitive requirements are not possible. Non-trivial refactoring would be // required to support transitive requirements. -enum EmeFeatureSupport { +enum class EmeFeatureSupport { // Invalid default value. - EME_FEATURE_INVALID, + INVALID, // Access to the feature is not supported at all. - EME_FEATURE_NOT_SUPPORTED, + NOT_SUPPORTED, // Access to the feature may be requested. - EME_FEATURE_REQUESTABLE, + REQUESTABLE, // Access to the feature cannot be blocked. - EME_FEATURE_ALWAYS_ENABLED, -}; - -// Used to query support for distinctive identifier and persistent state. -enum EmeFeatureRequirement { - EME_FEATURE_NOT_ALLOWED, - EME_FEATURE_OPTIONAL, - EME_FEATURE_REQUIRED, + ALWAYS_ENABLED, }; enum class EmeMediaType {
diff --git a/media/base/key_system_info.cc b/media/base/key_system_info.cc index f5ca112..8eec5b1c 100644 --- a/media/base/key_system_info.cc +++ b/media/base/key_system_info.cc
@@ -11,10 +11,10 @@ supported_codecs(EME_CODEC_NONE), max_audio_robustness(EmeRobustness::INVALID), max_video_robustness(EmeRobustness::INVALID), - persistent_license_support(EME_SESSION_TYPE_INVALID), - persistent_release_message_support(EME_SESSION_TYPE_INVALID), - persistent_state_support(EME_FEATURE_INVALID), - distinctive_identifier_support(EME_FEATURE_INVALID), + persistent_license_support(EmeSessionTypeSupport::INVALID), + persistent_release_message_support(EmeSessionTypeSupport::INVALID), + persistent_state_support(EmeFeatureSupport::INVALID), + distinctive_identifier_support(EmeFeatureSupport::INVALID), use_aes_decryptor(false) { }
diff --git a/media/base/key_systems.cc b/media/base/key_systems.cc index 7bbbde7..f8999257 100644 --- a/media/base/key_systems.cc +++ b/media/base/key_systems.cc
@@ -108,10 +108,11 @@ info.max_audio_robustness = EmeRobustness::EMPTY; info.max_video_robustness = EmeRobustness::EMPTY; - info.persistent_license_support = EME_SESSION_TYPE_NOT_SUPPORTED; - info.persistent_release_message_support = EME_SESSION_TYPE_NOT_SUPPORTED; - info.persistent_state_support = EME_FEATURE_NOT_SUPPORTED; - info.distinctive_identifier_support = EME_FEATURE_NOT_SUPPORTED; + info.persistent_license_support = EmeSessionTypeSupport::NOT_SUPPORTED; + info.persistent_release_message_support = + EmeSessionTypeSupport::NOT_SUPPORTED; + info.persistent_state_support = EmeFeatureSupport::NOT_SUPPORTED; + info.distinctive_identifier_support = EmeFeatureSupport::NOT_SUPPORTED; info.use_aes_decryptor = true; @@ -400,31 +401,34 @@ DCHECK(!info.key_system.empty()); DCHECK(info.max_audio_robustness != EmeRobustness::INVALID); DCHECK(info.max_video_robustness != EmeRobustness::INVALID); - DCHECK(info.persistent_license_support != EME_SESSION_TYPE_INVALID); - DCHECK(info.persistent_release_message_support != EME_SESSION_TYPE_INVALID); - DCHECK(info.persistent_state_support != EME_FEATURE_INVALID); - DCHECK(info.distinctive_identifier_support != EME_FEATURE_INVALID); + DCHECK(info.persistent_license_support != EmeSessionTypeSupport::INVALID); + DCHECK(info.persistent_release_message_support != + EmeSessionTypeSupport::INVALID); + DCHECK(info.persistent_state_support != EmeFeatureSupport::INVALID); + DCHECK(info.distinctive_identifier_support != EmeFeatureSupport::INVALID); // Supporting persistent state is a prerequsite for supporting persistent // sessions. - if (info.persistent_state_support == EME_FEATURE_NOT_SUPPORTED) { - DCHECK(info.persistent_license_support == EME_SESSION_TYPE_NOT_SUPPORTED); + if (info.persistent_state_support == EmeFeatureSupport::NOT_SUPPORTED) { + DCHECK(info.persistent_license_support == + EmeSessionTypeSupport::NOT_SUPPORTED); DCHECK(info.persistent_release_message_support == - EME_SESSION_TYPE_NOT_SUPPORTED); + EmeSessionTypeSupport::NOT_SUPPORTED); } // persistent-release-message sessions are not currently supported. // http://crbug.com/448888 DCHECK(info.persistent_release_message_support == - EME_SESSION_TYPE_NOT_SUPPORTED); + EmeSessionTypeSupport::NOT_SUPPORTED); // If distinctive identifiers are not supported, then no other features can // require them. - if (info.distinctive_identifier_support == EME_FEATURE_NOT_SUPPORTED) { + if (info.distinctive_identifier_support == + EmeFeatureSupport::NOT_SUPPORTED) { DCHECK(info.persistent_license_support != - EME_SESSION_TYPE_SUPPORTED_WITH_IDENTIFIER); + EmeSessionTypeSupport::SUPPORTED_WITH_IDENTIFIER); DCHECK(info.persistent_release_message_support != - EME_SESSION_TYPE_SUPPORTED_WITH_IDENTIFIER); + EmeSessionTypeSupport::SUPPORTED_WITH_IDENTIFIER); } // Distinctive identifiers and persistent state can only be reliably blocked @@ -438,8 +442,10 @@ can_block = true; #endif if (!can_block) { - DCHECK(info.distinctive_identifier_support == EME_FEATURE_ALWAYS_ENABLED); - DCHECK(info.persistent_state_support == EME_FEATURE_ALWAYS_ENABLED); + DCHECK(info.distinctive_identifier_support == + EmeFeatureSupport::ALWAYS_ENABLED); + DCHECK(info.persistent_state_support == + EmeFeatureSupport::ALWAYS_ENABLED); } DCHECK(!IsSupportedKeySystem(info.key_system)) @@ -776,7 +782,7 @@ concrete_key_system_map_.find(key_system); if (key_system_iter == concrete_key_system_map_.end()) { NOTREACHED(); - return EME_SESSION_TYPE_INVALID; + return EmeSessionTypeSupport::INVALID; } return key_system_iter->second.persistent_license_support; } @@ -789,7 +795,7 @@ concrete_key_system_map_.find(key_system); if (key_system_iter == concrete_key_system_map_.end()) { NOTREACHED(); - return EME_SESSION_TYPE_INVALID; + return EmeSessionTypeSupport::INVALID; } return key_system_iter->second.persistent_release_message_support; } @@ -802,7 +808,7 @@ concrete_key_system_map_.find(key_system); if (key_system_iter == concrete_key_system_map_.end()) { NOTREACHED(); - return EME_FEATURE_INVALID; + return EmeFeatureSupport::INVALID; } return key_system_iter->second.persistent_state_support; } @@ -815,7 +821,7 @@ concrete_key_system_map_.find(key_system); if (key_system_iter == concrete_key_system_map_.end()) { NOTREACHED(); - return EME_FEATURE_INVALID; + return EmeFeatureSupport::INVALID; } return key_system_iter->second.distinctive_identifier_support; }
diff --git a/media/base/key_systems_unittest.cc b/media/base/key_systems_unittest.cc index b80e6a1e..611c2b6 100644 --- a/media/base/key_systems_unittest.cc +++ b/media/base/key_systems_unittest.cc
@@ -193,10 +193,11 @@ system.supported_init_data_types = kInitDataTypeMaskWebM; system.max_audio_robustness = EmeRobustness::EMPTY; system.max_video_robustness = EmeRobustness::EMPTY; - system.persistent_license_support = EME_SESSION_TYPE_NOT_SUPPORTED; - system.persistent_release_message_support = EME_SESSION_TYPE_NOT_SUPPORTED; - system.persistent_state_support = EME_FEATURE_NOT_SUPPORTED; - system.distinctive_identifier_support = EME_FEATURE_NOT_SUPPORTED; + system.persistent_license_support = EmeSessionTypeSupport::NOT_SUPPORTED; + system.persistent_release_message_support = + EmeSessionTypeSupport::NOT_SUPPORTED; + system.persistent_state_support = EmeFeatureSupport::NOT_SUPPORTED; + system.distinctive_identifier_support = EmeFeatureSupport::NOT_SUPPORTED; system.use_aes_decryptor = true; key_systems->push_back(system); } @@ -210,10 +211,10 @@ ext.supported_init_data_types = kInitDataTypeMaskWebM; ext.max_audio_robustness = EmeRobustness::EMPTY; ext.max_video_robustness = EmeRobustness::EMPTY; - ext.persistent_license_support = EME_SESSION_TYPE_SUPPORTED; - ext.persistent_release_message_support = EME_SESSION_TYPE_NOT_SUPPORTED; - ext.persistent_state_support = EME_FEATURE_ALWAYS_ENABLED; - ext.distinctive_identifier_support = EME_FEATURE_ALWAYS_ENABLED; + ext.persistent_license_support = EmeSessionTypeSupport::SUPPORTED; + ext.persistent_release_message_support = EmeSessionTypeSupport::NOT_SUPPORTED; + ext.persistent_state_support = EmeFeatureSupport::ALWAYS_ENABLED; + ext.distinctive_identifier_support = EmeFeatureSupport::ALWAYS_ENABLED; ext.parent_key_system = kExternalParent; #if defined(ENABLE_PEPPER_CDMS) ext.pepper_type = "application/x-ppapi-external-cdm";
diff --git a/media/base/limits.h b/media/base/limits.h index 69a5b63..c3cd677 100644 --- a/media/base/limits.h +++ b/media/base/limits.h
@@ -52,6 +52,9 @@ kMinKeyIdLength = 1, kMaxKeyIdLength = 512, kMaxKeyIds = 128, + kMaxInitDataLength = 64 * 1024, // 64 KB + kMaxSessionResponseLength = 64 * 1024, // 64 KB + kMaxKeySystemLength = 256, }; } // namespace limits
diff --git a/media/base/mock_filters.h b/media/base/mock_filters.h index 37940e676..26700f6 100644 --- a/media/base/mock_filters.h +++ b/media/base/mock_filters.h
@@ -34,7 +34,7 @@ // Demuxer implementation. MOCK_METHOD3(Initialize, void(DemuxerHost* host, const PipelineStatusCB& cb, bool)); - MOCK_METHOD1(SetPlaybackRate, void(float playback_rate)); + MOCK_METHOD1(SetPlaybackRate, void(double playback_rate)); MOCK_METHOD2(Seek, void(base::TimeDelta time, const PipelineStatusCB& cb)); MOCK_METHOD0(Stop, void()); MOCK_METHOD0(OnAudioRendererDisabled, void()); @@ -179,7 +179,7 @@ const base::Closure& waiting_for_decryption_key_cb)); MOCK_METHOD1(Flush, void(const base::Closure& flush_cb)); MOCK_METHOD1(StartPlayingFrom, void(base::TimeDelta timestamp)); - MOCK_METHOD1(SetPlaybackRate, void(float playback_rate)); + MOCK_METHOD1(SetPlaybackRate, void(double playback_rate)); MOCK_METHOD1(SetVolume, void(float volume)); MOCK_METHOD0(GetMediaTime, base::TimeDelta()); MOCK_METHOD0(HasAudio, bool()); @@ -200,7 +200,7 @@ // TimeSource implementation. MOCK_METHOD0(StartTicking, void()); MOCK_METHOD0(StopTicking, void()); - MOCK_METHOD1(SetPlaybackRate, void(float)); + MOCK_METHOD1(SetPlaybackRate, void(double)); MOCK_METHOD1(SetMediaTime, void(base::TimeDelta)); MOCK_METHOD0(CurrentMediaTime, base::TimeDelta()); MOCK_METHOD1(GetWallClockTime, base::TimeTicks(base::TimeDelta));
diff --git a/media/base/pipeline.cc b/media/base/pipeline.cc index c39ddcdf..4a39704 100644 --- a/media/base/pipeline.cc +++ b/media/base/pipeline.cc
@@ -37,7 +37,7 @@ running_(false), did_loading_progress_(false), volume_(1.0f), - playback_rate_(0.0f), + playback_rate_(0.0), status_(PIPELINE_OK), state_(kCreated), renderer_ended_(false), @@ -121,13 +121,13 @@ return running_; } -float Pipeline::GetPlaybackRate() const { +double Pipeline::GetPlaybackRate() const { base::AutoLock auto_lock(lock_); return playback_rate_; } -void Pipeline::SetPlaybackRate(float playback_rate) { - if (playback_rate < 0.0f) +void Pipeline::SetPlaybackRate(double playback_rate) { + if (playback_rate < 0.0) return; base::AutoLock auto_lock(lock_); @@ -552,7 +552,7 @@ DoStop(base::Bind(&Pipeline::OnStopCompleted, weak_factory_.GetWeakPtr())); } -void Pipeline::PlaybackRateChangedTask(float playback_rate) { +void Pipeline::PlaybackRateChangedTask(double playback_rate) { DCHECK(task_runner_->BelongsToCurrentThread()); // Playback rate changes are only carried out while playing.
diff --git a/media/base/pipeline.h b/media/base/pipeline.h index f10bc393..b63be00 100644 --- a/media/base/pipeline.h +++ b/media/base/pipeline.h
@@ -141,17 +141,17 @@ bool IsRunning() const; // Gets the current playback rate of the pipeline. When the pipeline is - // started, the playback rate will be 0.0f. A rate of 1.0f indicates + // started, the playback rate will be 0.0. A rate of 1.0 indicates // that the pipeline is rendering the media at the standard rate. Valid - // values for playback rate are >= 0.0f. - float GetPlaybackRate() const; + // values for playback rate are >= 0.0. + double GetPlaybackRate() const; - // Attempt to adjust the playback rate. Setting a playback rate of 0.0f pauses - // all rendering of the media. A rate of 1.0f indicates a normal playback - // rate. Values for the playback rate must be greater than or equal to 0.0f. + // Attempt to adjust the playback rate. Setting a playback rate of 0.0 pauses + // all rendering of the media. A rate of 1.0 indicates a normal playback + // rate. Values for the playback rate must be greater than or equal to 0.0. // // TODO(scherkus): What about maximum rate? Does HTML5 specify a max? - void SetPlaybackRate(float playback_rate); + void SetPlaybackRate(double playback_rate); // Gets the current volume setting being used by the audio renderer. When // the pipeline is started, this value will be 1.0f. Valid values range @@ -242,7 +242,7 @@ void ErrorChangedTask(PipelineStatus error); // Carries out notifying filters that the playback rate has changed. - void PlaybackRateChangedTask(float playback_rate); + void PlaybackRateChangedTask(double playback_rate); // Carries out notifying filters that the volume has changed. void VolumeChangedTask(float volume); @@ -319,10 +319,10 @@ // filters. float volume_; - // Current playback rate (>= 0.0f). This value is set immediately via + // Current playback rate (>= 0.0). This value is set immediately via // SetPlaybackRate() and a task is dispatched on the task runner to notify // the filters. - float playback_rate_; + double playback_rate_; // Current duration as reported by |demuxer_|. base::TimeDelta duration_;
diff --git a/media/base/pipeline_unittest.cc b/media/base/pipeline_unittest.cc index 09d9fdd..d4f9fa0 100644 --- a/media/base/pipeline_unittest.cc +++ b/media/base/pipeline_unittest.cc
@@ -209,7 +209,7 @@ if (start_status == PIPELINE_OK) { EXPECT_CALL(callbacks_, OnMetadata(_)).WillOnce(SaveArg<0>(&metadata_)); - EXPECT_CALL(*renderer_, SetPlaybackRate(0.0f)); + EXPECT_CALL(*renderer_, SetPlaybackRate(0.0)); EXPECT_CALL(*renderer_, SetVolume(1.0f)); EXPECT_CALL(*renderer_, StartPlayingFrom(start_time_)) .WillOnce(SetBufferingState(&buffering_state_cb_, @@ -337,9 +337,9 @@ // Setting should still work. EXPECT_EQ(0.0f, pipeline_->GetPlaybackRate()); - pipeline_->SetPlaybackRate(-1.0f); + pipeline_->SetPlaybackRate(-1.0); EXPECT_EQ(0.0f, pipeline_->GetPlaybackRate()); - pipeline_->SetPlaybackRate(1.0f); + pipeline_->SetPlaybackRate(1.0); EXPECT_EQ(1.0f, pipeline_->GetPlaybackRate()); // Setting should still work. @@ -640,7 +640,7 @@ SetRendererExpectations(); StartPipelineAndExpect(PIPELINE_OK); - float playback_rate = 1.0f; + double playback_rate = 1.0; EXPECT_CALL(*renderer_, SetPlaybackRate(playback_rate)); pipeline_->SetPlaybackRate(playback_rate); message_loop_.RunUntilIdle(); @@ -675,7 +675,7 @@ EXPECT_TRUE(message_loop->IsIdleForTesting()); // Make calls on pipeline after error has occurred. - pipeline->SetPlaybackRate(0.5f); + pipeline->SetPlaybackRate(0.5); pipeline->SetVolume(0.5f); // No additional tasks should be queued as a result of these calls. @@ -877,7 +877,7 @@ EXPECT_CALL(callbacks_, OnMetadata(_)); // If we get here it's a successful initialization. - EXPECT_CALL(*renderer_, SetPlaybackRate(0.0f)); + EXPECT_CALL(*renderer_, SetPlaybackRate(0.0)); EXPECT_CALL(*renderer_, SetVolume(1.0f)); EXPECT_CALL(*renderer_, StartPlayingFrom(base::TimeDelta())) .WillOnce(SetBufferingState(&buffering_state_cb_,
diff --git a/media/base/renderer.h b/media/base/renderer.h index 31661c4..75372fba 100644 --- a/media/base/renderer.h +++ b/media/base/renderer.h
@@ -65,7 +65,7 @@ virtual void StartPlayingFrom(base::TimeDelta time) = 0; // Updates the current playback rate. The default playback rate should be 1. - virtual void SetPlaybackRate(float playback_rate) = 0; + virtual void SetPlaybackRate(double playback_rate) = 0; // Sets the output volume. The default volume should be 1. virtual void SetVolume(float volume) = 0;
diff --git a/media/base/time_delta_interpolator.cc b/media/base/time_delta_interpolator.cc index 11ba1cd..acff37e 100644 --- a/media/base/time_delta_interpolator.cc +++ b/media/base/time_delta_interpolator.cc
@@ -16,7 +16,7 @@ : tick_clock_(tick_clock), interpolating_(false), upper_bound_(kNoTimestamp()), - playback_rate_(1.0f) { + playback_rate_(1.0) { DCHECK(tick_clock_); } @@ -37,7 +37,7 @@ return lower_bound_; } -void TimeDeltaInterpolator::SetPlaybackRate(float playback_rate) { +void TimeDeltaInterpolator::SetPlaybackRate(double playback_rate) { lower_bound_ = GetInterpolatedTime(); reference_ = tick_clock_->NowTicks(); playback_rate_ = playback_rate;
diff --git a/media/base/time_delta_interpolator.h b/media/base/time_delta_interpolator.h index af7535d..7dbda6938 100644 --- a/media/base/time_delta_interpolator.h +++ b/media/base/time_delta_interpolator.h
@@ -42,7 +42,7 @@ // Sets a new rate at which to interpolate. // // |tick_clock| will be queried for a new reference time value. - void SetPlaybackRate(float playback_rate); + void SetPlaybackRate(double playback_rate); // Sets the two timestamps to interpolate between at |playback_rate_|. // |upper_bound| must be greater or equal to |lower_bound|. @@ -72,7 +72,7 @@ // |lower_bound_| and |upper_bound_|. base::TimeTicks reference_; - float playback_rate_; + double playback_rate_; DISALLOW_COPY_AND_ASSIGN(TimeDeltaInterpolator); };
diff --git a/media/base/time_delta_interpolator_unittest.cc b/media/base/time_delta_interpolator_unittest.cc index 04242f12..04ee8f7 100644 --- a/media/base/time_delta_interpolator_unittest.cc +++ b/media/base/time_delta_interpolator_unittest.cc
@@ -40,7 +40,7 @@ const base::TimeDelta kZero; const base::TimeDelta kTimeToAdvance = base::TimeDelta::FromSeconds(5); - interpolator_.SetPlaybackRate(2.0f); + interpolator_.SetPlaybackRate(2.0); EXPECT_EQ(kZero, interpolator_.StartInterpolating()); AdvanceSystemTime(kTimeToAdvance); EXPECT_EQ(2 * kTimeToAdvance, interpolator_.GetInterpolatedTime()); @@ -50,7 +50,7 @@ const base::TimeDelta kZero; const base::TimeDelta kTimeToAdvance = base::TimeDelta::FromSeconds(4); - interpolator_.SetPlaybackRate(0.5f); + interpolator_.SetPlaybackRate(0.5); EXPECT_EQ(kZero, interpolator_.StartInterpolating()); AdvanceSystemTime(kTimeToAdvance); EXPECT_EQ(kTimeToAdvance / 2, interpolator_.GetInterpolatedTime()); @@ -68,9 +68,9 @@ EXPECT_EQ(kZero, interpolator_.StartInterpolating()); AdvanceSystemTime(kPlayDuration1); - interpolator_.SetPlaybackRate(0.0f); + interpolator_.SetPlaybackRate(0.0); AdvanceSystemTime(kPlayDuration2); - interpolator_.SetPlaybackRate(1.0f); + interpolator_.SetPlaybackRate(1.0); AdvanceSystemTime(kPlayDuration3); EXPECT_EQ(kExpected, interpolator_.GetInterpolatedTime()); @@ -86,14 +86,14 @@ const base::TimeDelta kExpected = kPlayDuration1 / 2 + kPlayDuration2 + 2 * kPlayDuration3; - interpolator_.SetPlaybackRate(0.5f); + interpolator_.SetPlaybackRate(0.5); EXPECT_EQ(kZero, interpolator_.StartInterpolating()); AdvanceSystemTime(kPlayDuration1); - interpolator_.SetPlaybackRate(1.0f); + interpolator_.SetPlaybackRate(1.0); AdvanceSystemTime(kPlayDuration2); - interpolator_.SetPlaybackRate(2.0f); + interpolator_.SetPlaybackRate(2.0); AdvanceSystemTime(kPlayDuration3); EXPECT_EQ(kExpected, interpolator_.GetInterpolatedTime()); }
diff --git a/media/base/time_source.h b/media/base/time_source.h index 71fe858..d025de2a 100644 --- a/media/base/time_source.h +++ b/media/base/time_source.h
@@ -27,7 +27,7 @@ // Updates the current playback rate. It is expected that values from // CurrentMediaTime() will eventually reflect the new playback rate (e.g., the // media time will advance at half speed if the rate was set to 0.5f). - virtual void SetPlaybackRate(float playback_rate) = 0; + virtual void SetPlaybackRate(double playback_rate) = 0; // Sets the media time to start ticking from. Only valid to call while the // time source is not ticking.
diff --git a/media/base/user_input_monitor_win.cc b/media/base/user_input_monitor_win.cc index d06b82435a..6210236 100644 --- a/media/base/user_input_monitor_win.cc +++ b/media/base/user_input_monitor_win.cc
@@ -39,10 +39,10 @@ scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner, const scoped_refptr<UserInputMonitor::MouseListenerList>& mouse_listeners); - ~UserInputMonitorWinCore(); + ~UserInputMonitorWinCore() override; // DestructionObserver overrides. - virtual void WillDestroyCurrentMessageLoop() override; + void WillDestroyCurrentMessageLoop() override; size_t GetKeyPressCount() const; void StartMonitor(EventBitMask type); @@ -75,17 +75,17 @@ public: explicit UserInputMonitorWin( const scoped_refptr<base::SingleThreadTaskRunner>& ui_task_runner); - virtual ~UserInputMonitorWin(); + ~UserInputMonitorWin() override; // Public UserInputMonitor overrides. - virtual size_t GetKeyPressCount() const override; + size_t GetKeyPressCount() const override; private: // Private UserInputMonitor overrides. - virtual void StartKeyboardMonitoring() override; - virtual void StopKeyboardMonitoring() override; - virtual void StartMouseMonitoring() override; - virtual void StopMouseMonitoring() override; + void StartKeyboardMonitoring() override; + void StopKeyboardMonitoring() override; + void StartMouseMonitoring() override; + void StopMouseMonitoring() override; scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner_; UserInputMonitorWinCore* core_;
diff --git a/media/base/video_capture_types.cc b/media/base/video_capture_types.cc index 152801745..cc5f840 100644 --- a/media/base/video_capture_types.cc +++ b/media/base/video_capture_types.cc
@@ -49,6 +49,9 @@ break; case PIXEL_FORMAT_RGB32: case PIXEL_FORMAT_ARGB: + // GpuMemoryBuffer is an endianness-agnostic 32bpp pixel format until + // http://crbug.com/439520 is closed. + case PIXEL_FORMAT_GPUMEMORYBUFFER: result_frame_size *= 4; break; case PIXEL_FORMAT_MJPEG: @@ -95,6 +98,8 @@ return "YV12"; case PIXEL_FORMAT_TEXTURE: return "TEXTURE"; + case PIXEL_FORMAT_GPUMEMORYBUFFER: + return "GPUMEMORYBUFFER"; case PIXEL_FORMAT_MAX: break; }
diff --git a/media/base/video_capture_types.h b/media/base/video_capture_types.h index 817e942..463873f 100644 --- a/media/base/video_capture_types.h +++ b/media/base/video_capture_types.h
@@ -29,6 +29,7 @@ PIXEL_FORMAT_ARGB, PIXEL_FORMAT_MJPEG, PIXEL_FORMAT_TEXTURE, // Capture format as a GL texture. + PIXEL_FORMAT_GPUMEMORYBUFFER, PIXEL_FORMAT_UNKNOWN, // Color format not set. PIXEL_FORMAT_MAX, };
diff --git a/media/base/wall_clock_time_source.cc b/media/base/wall_clock_time_source.cc index e58162e..0369306 100644 --- a/media/base/wall_clock_time_source.cc +++ b/media/base/wall_clock_time_source.cc
@@ -12,7 +12,7 @@ WallClockTimeSource::WallClockTimeSource() : tick_clock_(new base::DefaultTickClock()), ticking_(false), - playback_rate_(1.0f) { + playback_rate_(1.0) { } WallClockTimeSource::~WallClockTimeSource() { @@ -35,7 +35,7 @@ reference_wall_ticks_ = tick_clock_->NowTicks(); } -void WallClockTimeSource::SetPlaybackRate(float playback_rate) { +void WallClockTimeSource::SetPlaybackRate(double playback_rate) { DVLOG(1) << __FUNCTION__ << "(" << playback_rate << ")"; base::AutoLock auto_lock(lock_); // Estimate current media time using old rate to use as a new base time for
diff --git a/media/base/wall_clock_time_source.h b/media/base/wall_clock_time_source.h index d3d210ef2..1de6518 100644 --- a/media/base/wall_clock_time_source.h +++ b/media/base/wall_clock_time_source.h
@@ -25,7 +25,7 @@ // TimeSource implementation. void StartTicking() override; void StopTicking() override; - void SetPlaybackRate(float playback_rate) override; + void SetPlaybackRate(double playback_rate) override; void SetMediaTime(base::TimeDelta time) override; base::TimeDelta CurrentMediaTime() override; base::TimeTicks GetWallClockTime(base::TimeDelta time) override;
diff --git a/media/blink/buffered_data_source.cc b/media/blink/buffered_data_source.cc index ed93848..d5f32b1 100644 --- a/media/blink/buffered_data_source.cc +++ b/media/blink/buffered_data_source.cc
@@ -189,11 +189,11 @@ frame_ = NULL; } -void BufferedDataSource::MediaPlaybackRateChanged(float playback_rate) { +void BufferedDataSource::MediaPlaybackRateChanged(double playback_rate) { DCHECK(render_task_runner_->BelongsToCurrentThread()); DCHECK(loader_.get()); - if (playback_rate < 0.0f) + if (playback_rate < 0.0) return; playback_rate_ = playback_rate;
diff --git a/media/blink/buffered_data_source.h b/media/blink/buffered_data_source.h index e149bb06f..32f14816 100644 --- a/media/blink/buffered_data_source.h +++ b/media/blink/buffered_data_source.h
@@ -100,7 +100,7 @@ // Notifies changes in playback state for controlling media buffering // behavior. - void MediaPlaybackRateChanged(float playback_rate); + void MediaPlaybackRateChanged(double playback_rate); void MediaIsPlaying(); void MediaIsPaused(); bool media_has_played() const { return media_has_played_; } @@ -229,7 +229,7 @@ int bitrate_; // Current playback rate. - float playback_rate_; + double playback_rate_; scoped_refptr<MediaLog> media_log_;
diff --git a/media/blink/buffered_data_source_unittest.cc b/media/blink/buffered_data_source_unittest.cc index eed8674..3776fd6 100644 --- a/media/blink/buffered_data_source_unittest.cc +++ b/media/blink/buffered_data_source_unittest.cc
@@ -234,9 +234,9 @@ return loader()->defer_strategy_; } int data_source_bitrate() { return data_source_->bitrate_; } - int data_source_playback_rate() { return data_source_->playback_rate_; } + double data_source_playback_rate() { return data_source_->playback_rate_; } int loader_bitrate() { return loader()->bitrate_; } - int loader_playback_rate() { return loader()->playback_rate_; } + double loader_playback_rate() { return loader()->playback_rate_; } bool is_local_source() { return data_source_->assume_fully_buffered(); } void set_might_be_reused_from_cache_in_future(bool value) { loader()->might_be_reused_from_cache_in_future_ = value; @@ -560,9 +560,9 @@ EXPECT_EQ(BufferedResourceLoader::kCapacityDefer, defer_strategy()); EXPECT_EQ(0, data_source_bitrate()); - EXPECT_EQ(0.0f, data_source_playback_rate()); + EXPECT_EQ(0.0, data_source_playback_rate()); EXPECT_EQ(0, loader_bitrate()); - EXPECT_EQ(0.0f, loader_playback_rate()); + EXPECT_EQ(0.0, loader_playback_rate()); EXPECT_TRUE(data_source_->loading()); Stop(); @@ -594,10 +594,10 @@ TEST_F(BufferedDataSourceTest, MediaPlaybackRateChanged) { InitializeWith206Response(); - data_source_->MediaPlaybackRateChanged(2.0f); + data_source_->MediaPlaybackRateChanged(2.0); message_loop_.RunUntilIdle(); - EXPECT_EQ(2.0f, data_source_playback_rate()); - EXPECT_EQ(2.0f, loader_playback_rate()); + EXPECT_EQ(2.0, data_source_playback_rate()); + EXPECT_EQ(2.0, loader_playback_rate()); // Read so far ahead to cause the loader to get recreated. BufferedResourceLoader* old_loader = loader();
diff --git a/media/blink/buffered_resource_loader.cc b/media/blink/buffered_resource_loader.cc index b2598ea..51ec8fe 100644 --- a/media/blink/buffered_resource_loader.cc +++ b/media/blink/buffered_resource_loader.cc
@@ -54,12 +54,12 @@ // Computes the suggested backward and forward capacity for the buffer // if one wants to play at |playback_rate| * the natural playback speed. // Use a value of 0 for |bitrate| if it is unknown. -static void ComputeTargetBufferWindow(float playback_rate, int bitrate, +static void ComputeTargetBufferWindow(double playback_rate, int bitrate, int* out_backward_capacity, int* out_forward_capacity) { static const int kDefaultBitrate = 200 * 1024 * 8; // 200 Kbps. static const int kMaxBitrate = 20 * kMegabyte * 8; // 20 Mbps. - static const float kMaxPlaybackRate = 25.0; + static const double kMaxPlaybackRate = 25.0; static const int kTargetSecondsBufferedAhead = 10; static const int kTargetSecondsBufferedBehind = 2; @@ -71,12 +71,12 @@ // Only scale the buffer window for playback rates greater than 1.0 in // magnitude and clamp to prevent overflow. bool backward_playback = false; - if (playback_rate < 0.0f) { + if (playback_rate < 0.0) { backward_playback = true; - playback_rate *= -1.0f; + playback_rate *= -1.0; } - playback_rate = std::max(playback_rate, 1.0f); + playback_rate = std::max(playback_rate, 1.0); playback_rate = std::min(playback_rate, kMaxPlaybackRate); int bytes_per_second = (bitrate / 8.0) * playback_rate; @@ -101,7 +101,7 @@ int64 last_byte_position, DeferStrategy strategy, int bitrate, - float playback_rate, + double playback_rate, MediaLog* media_log) : buffer_(kMinBufferCapacity, kMinBufferCapacity), loader_failed_(false), @@ -571,7 +571,7 @@ UpdateDeferBehavior(); } -void BufferedResourceLoader::SetPlaybackRate(float playback_rate) { +void BufferedResourceLoader::SetPlaybackRate(double playback_rate) { playback_rate_ = playback_rate; // This is a pause so don't bother updating the buffer window as we'll likely
diff --git a/media/blink/buffered_resource_loader.h b/media/blink/buffered_resource_loader.h index 2b9cf579..cb02720 100644 --- a/media/blink/buffered_resource_loader.h +++ b/media/blink/buffered_resource_loader.h
@@ -85,7 +85,7 @@ int64 last_byte_position, DeferStrategy strategy, int bitrate, - float playback_rate, + double playback_rate, MediaLog* media_log); virtual ~BufferedResourceLoader(); @@ -179,7 +179,7 @@ // Sets the playback rate to the given value and updates buffer window // accordingly. - void SetPlaybackRate(float playback_rate); + void SetPlaybackRate(double playback_rate); // Sets the bitrate to the given value and updates buffer window // accordingly. @@ -314,7 +314,7 @@ int bitrate_; // Playback rate of the media. - float playback_rate_; + double playback_rate_; scoped_refptr<MediaLog> media_log_;
diff --git a/media/blink/buffered_resource_loader_unittest.cc b/media/blink/buffered_resource_loader_unittest.cc index f046d60..1fdd6d8 100644 --- a/media/blink/buffered_resource_loader_unittest.cc +++ b/media/blink/buffered_resource_loader_unittest.cc
@@ -1068,7 +1068,7 @@ TEST_F(BufferedResourceLoaderTest, BufferWindow_PlaybackRate_BelowLowerBound) { Initialize(kHttpUrl, -1, -1); Start(); - loader_->SetPlaybackRate(0.1f); + loader_->SetPlaybackRate(0.1); CheckBufferWindowBounds(); StopWhenLoad(); }
diff --git a/media/blink/key_system_config_selector.cc b/media/blink/key_system_config_selector.cc index 769f973..67a13e2 100644 --- a/media/blink/key_system_config_selector.cc +++ b/media/blink/key_system_config_selector.cc
@@ -20,33 +20,21 @@ namespace media { +using EmeFeatureRequirement = + blink::WebMediaKeySystemConfiguration::Requirement; + namespace { -static EmeFeatureRequirement ConvertRequirement( - blink::WebMediaKeySystemConfiguration::Requirement requirement) { - switch (requirement) { - case blink::WebMediaKeySystemConfiguration::Requirement::Required: - return EME_FEATURE_REQUIRED; - case blink::WebMediaKeySystemConfiguration::Requirement::Optional: - return EME_FEATURE_OPTIONAL; - case blink::WebMediaKeySystemConfiguration::Requirement::NotAllowed: - return EME_FEATURE_NOT_ALLOWED; - } - - NOTREACHED(); - return EME_FEATURE_NOT_ALLOWED; -} - static EmeConfigRule GetSessionTypeConfigRule(EmeSessionTypeSupport support) { switch (support) { - case EME_SESSION_TYPE_INVALID: + case EmeSessionTypeSupport::INVALID: NOTREACHED(); return EmeConfigRule::NOT_SUPPORTED; - case EME_SESSION_TYPE_NOT_SUPPORTED: + case EmeSessionTypeSupport::NOT_SUPPORTED: return EmeConfigRule::NOT_SUPPORTED; - case EME_SESSION_TYPE_SUPPORTED_WITH_IDENTIFIER: + case EmeSessionTypeSupport::SUPPORTED_WITH_IDENTIFIER: return EmeConfigRule::IDENTIFIER_AND_PERSISTENCE_REQUIRED; - case EME_SESSION_TYPE_SUPPORTED: + case EmeSessionTypeSupport::SUPPORTED: return EmeConfigRule::PERSISTENCE_REQUIRED; } NOTREACHED(); @@ -56,7 +44,7 @@ static EmeConfigRule GetDistinctiveIdentifierConfigRule( EmeFeatureSupport support, EmeFeatureRequirement requirement) { - if (support == EME_FEATURE_INVALID) { + if (support == EmeFeatureSupport::INVALID) { NOTREACHED(); return EmeConfigRule::NOT_SUPPORTED; } @@ -70,24 +58,24 @@ // NOT_SUPPORTED I_NOT_ALLOWED I_NOT_ALLOWED NOT_SUPPORTED // REQUESTABLE I_NOT_ALLOWED SUPPORTED I_REQUIRED // ALWAYS_ENABLED NOT_SUPPORTED I_REQUIRED I_REQUIRED - DCHECK(support == EME_FEATURE_NOT_SUPPORTED || - support == EME_FEATURE_REQUESTABLE || - support == EME_FEATURE_ALWAYS_ENABLED); - DCHECK(requirement == EME_FEATURE_NOT_ALLOWED || - requirement == EME_FEATURE_OPTIONAL || - requirement == EME_FEATURE_REQUIRED); - if ((support == EME_FEATURE_NOT_SUPPORTED && - requirement == EME_FEATURE_REQUIRED) || - (support == EME_FEATURE_ALWAYS_ENABLED && - requirement == EME_FEATURE_NOT_ALLOWED)) { + DCHECK(support == EmeFeatureSupport::NOT_SUPPORTED || + support == EmeFeatureSupport::REQUESTABLE || + support == EmeFeatureSupport::ALWAYS_ENABLED); + DCHECK(requirement == EmeFeatureRequirement::NotAllowed || + requirement == EmeFeatureRequirement::Optional || + requirement == EmeFeatureRequirement::Required); + if ((support == EmeFeatureSupport::NOT_SUPPORTED && + requirement == EmeFeatureRequirement::Required) || + (support == EmeFeatureSupport::ALWAYS_ENABLED && + requirement == EmeFeatureRequirement::NotAllowed)) { return EmeConfigRule::NOT_SUPPORTED; } - if (support == EME_FEATURE_REQUESTABLE && - requirement == EME_FEATURE_OPTIONAL) { + if (support == EmeFeatureSupport::REQUESTABLE && + requirement == EmeFeatureRequirement::Optional) { return EmeConfigRule::SUPPORTED; } - if (support == EME_FEATURE_NOT_SUPPORTED || - requirement == EME_FEATURE_NOT_ALLOWED) { + if (support == EmeFeatureSupport::NOT_SUPPORTED || + requirement == EmeFeatureRequirement::NotAllowed) { return EmeConfigRule::IDENTIFIER_NOT_ALLOWED; } return EmeConfigRule::IDENTIFIER_REQUIRED; @@ -96,7 +84,7 @@ static EmeConfigRule GetPersistentStateConfigRule( EmeFeatureSupport support, EmeFeatureRequirement requirement) { - if (support == EME_FEATURE_INVALID) { + if (support == EmeFeatureSupport::INVALID) { NOTREACHED(); return EmeConfigRule::NOT_SUPPORTED; } @@ -113,24 +101,24 @@ // NOT_SUPPORTED P_NOT_ALLOWED P_NOT_ALLOWED NOT_SUPPORTED // REQUESTABLE P_NOT_ALLOWED SUPPORTED P_REQUIRED // ALWAYS_ENABLED NOT_SUPPORTED P_REQUIRED P_REQUIRED - DCHECK(support == EME_FEATURE_NOT_SUPPORTED || - support == EME_FEATURE_REQUESTABLE || - support == EME_FEATURE_ALWAYS_ENABLED); - DCHECK(requirement == EME_FEATURE_NOT_ALLOWED || - requirement == EME_FEATURE_OPTIONAL || - requirement == EME_FEATURE_REQUIRED); - if ((support == EME_FEATURE_NOT_SUPPORTED && - requirement == EME_FEATURE_REQUIRED) || - (support == EME_FEATURE_ALWAYS_ENABLED && - requirement == EME_FEATURE_NOT_ALLOWED)) { + DCHECK(support == EmeFeatureSupport::NOT_SUPPORTED || + support == EmeFeatureSupport::REQUESTABLE || + support == EmeFeatureSupport::ALWAYS_ENABLED); + DCHECK(requirement == EmeFeatureRequirement::NotAllowed || + requirement == EmeFeatureRequirement::Optional || + requirement == EmeFeatureRequirement::Required); + if ((support == EmeFeatureSupport::NOT_SUPPORTED && + requirement == EmeFeatureRequirement::Required) || + (support == EmeFeatureSupport::ALWAYS_ENABLED && + requirement == EmeFeatureRequirement::NotAllowed)) { return EmeConfigRule::NOT_SUPPORTED; } - if (support == EME_FEATURE_REQUESTABLE && - requirement == EME_FEATURE_OPTIONAL) { + if (support == EmeFeatureSupport::REQUESTABLE && + requirement == EmeFeatureRequirement::Optional) { return EmeConfigRule::SUPPORTED; } - if (support == EME_FEATURE_NOT_SUPPORTED || - requirement == EME_FEATURE_NOT_ALLOWED) { + if (support == EmeFeatureSupport::NOT_SUPPORTED || + requirement == EmeFeatureRequirement::NotAllowed) { return EmeConfigRule::PERSISTENCE_NOT_ALLOWED; } return EmeConfigRule::PERSISTENCE_REQUIRED; @@ -426,7 +414,7 @@ // permission has already been denied. This would happen anyway at step 11. EmeConfigRule di_rule = GetDistinctiveIdentifierConfigRule( key_systems_->GetDistinctiveIdentifierSupport(key_system), - ConvertRequirement(candidate.distinctiveIdentifier)); + candidate.distinctiveIdentifier); if (!config_state->IsRuleSupported(di_rule)) { DVLOG(2) << "Rejecting requested configuration because " << "the distinctiveIdentifier requirement was not supported."; @@ -448,7 +436,7 @@ // combination with accumulated configuration, return null. EmeConfigRule ps_rule = GetPersistentStateConfigRule( key_systems_->GetPersistentStateSupport(key_system), - ConvertRequirement(candidate.persistentState)); + candidate.persistentState); if (!config_state->IsRuleSupported(ps_rule)) { DVLOG(2) << "Rejecting requested configuration because " << "the persistentState requirement was not supported."; @@ -565,10 +553,10 @@ blink::WebMediaKeySystemConfiguration::Requirement::Optional) { EmeConfigRule not_allowed_rule = GetDistinctiveIdentifierConfigRule( key_systems_->GetDistinctiveIdentifierSupport(key_system), - EME_FEATURE_NOT_ALLOWED); + EmeFeatureRequirement::NotAllowed); EmeConfigRule required_rule = GetDistinctiveIdentifierConfigRule( key_systems_->GetDistinctiveIdentifierSupport(key_system), - EME_FEATURE_REQUIRED); + EmeFeatureRequirement::Required); bool not_allowed_supported = config_state->IsRuleSupported(not_allowed_rule); bool required_supported = config_state->IsRuleSupported(required_rule); @@ -605,10 +593,10 @@ blink::WebMediaKeySystemConfiguration::Requirement::Optional) { EmeConfigRule not_allowed_rule = GetPersistentStateConfigRule( key_systems_->GetPersistentStateSupport(key_system), - EME_FEATURE_NOT_ALLOWED); + EmeFeatureRequirement::NotAllowed); EmeConfigRule required_rule = GetPersistentStateConfigRule( key_systems_->GetPersistentStateSupport(key_system), - EME_FEATURE_REQUIRED); + EmeFeatureRequirement::Required); // |distinctiveIdentifier| should not be affected after it is decided. DCHECK(not_allowed_rule == EmeConfigRule::NOT_SUPPORTED || not_allowed_rule == EmeConfigRule::PERSISTENCE_NOT_ALLOWED);
diff --git a/media/blink/key_system_config_selector_unittest.cc b/media/blink/key_system_config_selector_unittest.cc index fc6d99a..762db78 100644 --- a/media/blink/key_system_config_selector_unittest.cc +++ b/media/blink/key_system_config_selector_unittest.cc
@@ -124,15 +124,16 @@ InitDataTypeMask init_data_types = kInitDataTypeMaskNone; // INVALID so that they must be set in any test that needs them. - EmeSessionTypeSupport persistent_license = EME_SESSION_TYPE_INVALID; - EmeSessionTypeSupport persistent_release_message = EME_SESSION_TYPE_INVALID; + EmeSessionTypeSupport persistent_license = EmeSessionTypeSupport::INVALID; + EmeSessionTypeSupport persistent_release_message = + EmeSessionTypeSupport::INVALID; // Every test implicitly requires these, so they must be set. They are set to // values that are likely to cause tests to fail if they are accidentally // depended on. Test cases explicitly depending on them should set them, as // the default values may be changed. - EmeFeatureSupport persistent_state = EME_FEATURE_NOT_SUPPORTED; - EmeFeatureSupport distinctive_identifier = EME_FEATURE_REQUESTABLE; + EmeFeatureSupport persistent_state = EmeFeatureSupport::NOT_SUPPORTED; + EmeFeatureSupport distinctive_identifier = EmeFeatureSupport::REQUESTABLE; }; class FakeMediaPermission : public MediaPermission { @@ -327,7 +328,7 @@ // --- distinctiveIdentifier --- TEST_F(KeySystemConfigSelectorTest, DistinctiveIdentifier_Default) { - key_systems_->distinctive_identifier = EME_FEATURE_REQUESTABLE; + key_systems_->distinctive_identifier = EmeFeatureSupport::REQUESTABLE; blink::WebMediaKeySystemConfiguration config; config.distinctiveIdentifier = @@ -341,7 +342,7 @@ TEST_F(KeySystemConfigSelectorTest, DistinctiveIdentifier_Forced) { media_permission_->is_granted = true; - key_systems_->distinctive_identifier = EME_FEATURE_ALWAYS_ENABLED; + key_systems_->distinctive_identifier = EmeFeatureSupport::ALWAYS_ENABLED; blink::WebMediaKeySystemConfiguration config; config.distinctiveIdentifier = @@ -354,7 +355,7 @@ } TEST_F(KeySystemConfigSelectorTest, DistinctiveIdentifier_Blocked) { - key_systems_->distinctive_identifier = EME_FEATURE_NOT_SUPPORTED; + key_systems_->distinctive_identifier = EmeFeatureSupport::NOT_SUPPORTED; blink::WebMediaKeySystemConfiguration config; config.distinctiveIdentifier = @@ -366,7 +367,7 @@ TEST_F(KeySystemConfigSelectorTest, DistinctiveIdentifier_RequestsPermission) { media_permission_->is_granted = true; - key_systems_->distinctive_identifier = EME_FEATURE_REQUESTABLE; + key_systems_->distinctive_identifier = EmeFeatureSupport::REQUESTABLE; blink::WebMediaKeySystemConfiguration config; config.distinctiveIdentifier = @@ -380,7 +381,7 @@ TEST_F(KeySystemConfigSelectorTest, DistinctiveIdentifier_RespectsPermission) { media_permission_->is_granted = false; - key_systems_->distinctive_identifier = EME_FEATURE_REQUESTABLE; + key_systems_->distinctive_identifier = EmeFeatureSupport::REQUESTABLE; blink::WebMediaKeySystemConfiguration config; config.distinctiveIdentifier = @@ -393,7 +394,7 @@ // --- persistentState --- TEST_F(KeySystemConfigSelectorTest, PersistentState_Default) { - key_systems_->persistent_state = EME_FEATURE_REQUESTABLE; + key_systems_->persistent_state = EmeFeatureSupport::REQUESTABLE; blink::WebMediaKeySystemConfiguration config; config.persistentState = @@ -406,7 +407,7 @@ } TEST_F(KeySystemConfigSelectorTest, PersistentState_Forced) { - key_systems_->persistent_state = EME_FEATURE_ALWAYS_ENABLED; + key_systems_->persistent_state = EmeFeatureSupport::ALWAYS_ENABLED; blink::WebMediaKeySystemConfiguration config; config.persistentState = @@ -419,7 +420,7 @@ } TEST_F(KeySystemConfigSelectorTest, PersistentState_Blocked) { - key_systems_->persistent_state = EME_FEATURE_ALWAYS_ENABLED; + key_systems_->persistent_state = EmeFeatureSupport::ALWAYS_ENABLED; blink::WebMediaKeySystemConfiguration config; config.persistentState = @@ -442,8 +443,8 @@ TEST_F(KeySystemConfigSelectorTest, SessionTypes_SubsetSupported) { // Allow persistent state, as it would be required to be successful. - key_systems_->persistent_state = EME_FEATURE_REQUESTABLE; - key_systems_->persistent_license = EME_SESSION_TYPE_NOT_SUPPORTED; + key_systems_->persistent_state = EmeFeatureSupport::REQUESTABLE; + key_systems_->persistent_license = EmeSessionTypeSupport::NOT_SUPPORTED; std::vector<blink::WebEncryptedMediaSessionType> session_types; session_types.push_back(blink::WebEncryptedMediaSessionType::Temporary); @@ -460,8 +461,8 @@ TEST_F(KeySystemConfigSelectorTest, SessionTypes_AllSupported) { // Allow persistent state, and expect it to be required. - key_systems_->persistent_state = EME_FEATURE_REQUESTABLE; - key_systems_->persistent_license = EME_SESSION_TYPE_SUPPORTED; + key_systems_->persistent_state = EmeFeatureSupport::REQUESTABLE; + key_systems_->persistent_license = EmeSessionTypeSupport::SUPPORTED; std::vector<blink::WebEncryptedMediaSessionType> session_types; session_types.push_back(blink::WebEncryptedMediaSessionType::Temporary); @@ -487,9 +488,10 @@ TEST_F(KeySystemConfigSelectorTest, SessionTypes_PermissionCanBeRequired) { media_permission_->is_granted = true; - key_systems_->distinctive_identifier = EME_FEATURE_REQUESTABLE; - key_systems_->persistent_state = EME_FEATURE_REQUESTABLE; - key_systems_->persistent_license = EME_SESSION_TYPE_SUPPORTED_WITH_IDENTIFIER; + key_systems_->distinctive_identifier = EmeFeatureSupport::REQUESTABLE; + key_systems_->persistent_state = EmeFeatureSupport::REQUESTABLE; + key_systems_->persistent_license = + EmeSessionTypeSupport::SUPPORTED_WITH_IDENTIFIER; std::vector<blink::WebEncryptedMediaSessionType> session_types; session_types.push_back( @@ -636,7 +638,7 @@ TEST_F(KeySystemConfigSelectorTest, VideoCapabilities_Robustness_PermissionCanBeRequired) { media_permission_->is_granted = true; - key_systems_->distinctive_identifier = EME_FEATURE_REQUESTABLE; + key_systems_->distinctive_identifier = EmeFeatureSupport::REQUESTABLE; std::vector<blink::WebMediaKeySystemMediaCapability> video_capabilities(1); video_capabilities[0].contentType = "a"; @@ -656,7 +658,7 @@ TEST_F(KeySystemConfigSelectorTest, VideoCapabilities_Robustness_PermissionCanBeRecommended) { media_permission_->is_granted = false; - key_systems_->distinctive_identifier = EME_FEATURE_REQUESTABLE; + key_systems_->distinctive_identifier = EmeFeatureSupport::REQUESTABLE; std::vector<blink::WebMediaKeySystemMediaCapability> video_capabilities(1); video_capabilities[0].contentType = "a"; @@ -725,7 +727,7 @@ TEST_F(KeySystemConfigSelectorTest, Configurations_FirstRequiresPermission_Allowed) { media_permission_->is_granted = true; - key_systems_->distinctive_identifier = EME_FEATURE_REQUESTABLE; + key_systems_->distinctive_identifier = EmeFeatureSupport::REQUESTABLE; blink::WebMediaKeySystemConfiguration config1; config1.label = "a"; @@ -744,7 +746,7 @@ TEST_F(KeySystemConfigSelectorTest, Configurations_FirstRequiresPermission_Rejected) { media_permission_->is_granted = false; - key_systems_->distinctive_identifier = EME_FEATURE_REQUESTABLE; + key_systems_->distinctive_identifier = EmeFeatureSupport::REQUESTABLE; blink::WebMediaKeySystemConfiguration config1; config1.label = "a";
diff --git a/media/blink/webcontentdecryptionmodulesession_impl.cc b/media/blink/webcontentdecryptionmodulesession_impl.cc index 643aa948..94afa0e 100644 --- a/media/blink/webcontentdecryptionmodulesession_impl.cc +++ b/media/blink/webcontentdecryptionmodulesession_impl.cc
@@ -13,11 +13,14 @@ #include "media/base/cdm_key_information.h" #include "media/base/cdm_promise.h" #include "media/base/key_systems.h" +#include "media/base/limits.h" #include "media/base/media_keys.h" #include "media/blink/cdm_result_promise.h" #include "media/blink/cdm_session_adapter.h" #include "media/blink/new_session_cdm_result_promise.h" #include "media/blink/webmediaplayer_util.h" +#include "media/cdm/cenc_utils.h" +#include "media/cdm/json_web_key.h" #include "third_party/WebKit/public/platform/WebData.h" #include "third_party/WebKit/public/platform/WebEncryptedMediaKeyInformation.h" #include "third_party/WebKit/public/platform/WebString.h" @@ -91,6 +94,60 @@ return MediaKeys::TEMPORARY_SESSION; } +static bool SanitizeInitData(EmeInitDataType init_data_type, + const unsigned char* init_data, + size_t init_data_length, + std::vector<uint8>* sanitized_init_data, + std::string* error_message) { + if (init_data_length > limits::kMaxInitDataLength) { + error_message->assign("Initialization data too long."); + return false; + } + + switch (init_data_type) { + case EmeInitDataType::WEBM: + sanitized_init_data->assign(init_data, init_data + init_data_length); + return true; + + case EmeInitDataType::CENC: + if (!ValidatePsshInput(init_data, init_data_length)) { + error_message->assign("Initialization data for CENC is incorrect."); + return false; + } + + sanitized_init_data->assign(init_data, init_data + init_data_length); + return true; + + case EmeInitDataType::KEYIDS: { + // Extract the keys and then rebuild the message. This ensures that any + // extra data in the provided JSON is dropped. + std::string init_data_string(init_data, init_data + init_data_length); + KeyIdList key_ids; + if (!ExtractKeyIdsFromKeyIdsInitData(init_data_string, &key_ids, + error_message)) + return false; + + for (const auto& key_id : key_ids) { + if (key_id.size() < limits::kMinKeyIdLength || + key_id.size() > limits::kMaxKeyIdLength) { + error_message->assign("Incorrect key size."); + return false; + } + } + + CreateKeyIdsInitData(key_ids, sanitized_init_data); + return true; + } + + case EmeInitDataType::UNKNOWN: + break; + } + + NOTREACHED(); + error_message->assign("Initialization data type is not supported."); + return false; +} + WebContentDecryptionModuleSessionImpl::WebContentDecryptionModuleSessionImpl( const scoped_refptr<CdmSessionAdapter>& adapter) : adapter_(adapter), is_closed_(false), weak_ptr_factory_(this) { @@ -118,7 +175,7 @@ blink::WebContentDecryptionModuleResult result) { DCHECK(session_id_.empty()); - // Step 5 from https://w3c.github.io/encrypted-media/#generateRequest. + // From https://w3c.github.io/encrypted-media/#generateRequest. // 5. If the Key System implementation represented by this object's cdm // implementation value does not support initDataType as an Initialization // Data Type, return a promise rejected with a new DOMException whose name @@ -134,9 +191,42 @@ return; } + // 9.1 If the init data is not valid for initDataType, reject promise with a + // new DOMException whose name is InvalidAccessError. + // 9.2 Let sanitized init data be a validated and sanitized version of init + // data. The user agent must thoroughly validate the Initialization Data + // before passing it to the CDM. This includes verifying that the length + // and values of fields are reasonable, verifying that values are within + // reasonable limits, and stripping irrelevant, unsupported, or unknown + // data or fields. It is recommended that user agents pre-parse, sanitize, + // and/or generate a fully sanitized version of the Initialization Data. + // If the Initialization Data format specified by initDataType support + // multiple entries, the user agent should remove entries that are not + // needed by the CDM. + // 9.3 If the previous step failed, reject promise with a new DOMException + // whose name is InvalidAccessError. + std::vector<uint8> sanitized_init_data; + std::string message; + if (!SanitizeInitData(eme_init_data_type, init_data, init_data_length, + &sanitized_init_data, &message)) { + result.completeWithError( + blink::WebContentDecryptionModuleExceptionInvalidAccessError, 0, + blink::WebString::fromUTF8(message)); + return; + } + + // 9.4 Let session id be the empty string. + // (Done in constructor.) + + // 9.5 Let message be null. + // (Done by CDM.) + + // 9.6 Let cdm be the CDM instance represented by this object's cdm + // instance value. + // 9.7 Use the cdm to execute the following steps: adapter_->InitializeNewSession( - eme_init_data_type, init_data, - base::saturated_cast<int>(init_data_length), + eme_init_data_type, vector_as_array(&sanitized_init_data), + base::saturated_cast<int>(sanitized_init_data.size()), convertSessionType(session_type), scoped_ptr<NewSessionCdmPromise>(new NewSessionCdmResultPromise( result, adapter_->GetKeySystemUMAPrefix() + kGenerateRequestUMAName, @@ -145,16 +235,6 @@ base::Unretained(this))))); } -// TODO(jrummell): Remove this. http://crbug.com/418239. -void WebContentDecryptionModuleSessionImpl::initializeNewSession( - const blink::WebString& init_data_type, - const uint8* init_data, - size_t init_data_length, - const blink::WebString& session_type, - blink::WebContentDecryptionModuleResult result) { - NOTREACHED(); -} - void WebContentDecryptionModuleSessionImpl::load( const blink::WebString& session_id, blink::WebContentDecryptionModuleResult result) { @@ -207,8 +287,8 @@ MediaKeys::MessageType message_type, const std::vector<uint8>& message) { DCHECK(client_) << "Client not set before message event"; - client_->message(convertMessageType(message_type), - message.empty() ? NULL : &message[0], message.size()); + client_->message(convertMessageType(message_type), vector_as_array(&message), + message.size()); } void WebContentDecryptionModuleSessionImpl::OnSessionKeysChange(
diff --git a/media/blink/webcontentdecryptionmodulesession_impl.h b/media/blink/webcontentdecryptionmodulesession_impl.h index 881c07e13..2f00ff5 100644 --- a/media/blink/webcontentdecryptionmodulesession_impl.h +++ b/media/blink/webcontentdecryptionmodulesession_impl.h
@@ -38,12 +38,6 @@ size_t initDataLength, blink::WebEncryptedMediaSessionType session_type, blink::WebContentDecryptionModuleResult result); - virtual void initializeNewSession( - const blink::WebString& init_data_type, - const uint8* init_data, - size_t init_data_length, - const blink::WebString& session_type, - blink::WebContentDecryptionModuleResult result); virtual void load(const blink::WebString& session_id, blink::WebContentDecryptionModuleResult result); virtual void update(const uint8* response,
diff --git a/media/blink/webmediaplayer_impl.cc b/media/blink/webmediaplayer_impl.cc index 8fa7a7f5..ac67b21 100644 --- a/media/blink/webmediaplayer_impl.cc +++ b/media/blink/webmediaplayer_impl.cc
@@ -122,7 +122,7 @@ opaque_(false), paused_(true), seeking_(false), - playback_rate_(0.0f), + playback_rate_(0.0), ended_(false), pending_seek_(false), pending_seek_seconds_(0.0f), @@ -269,7 +269,7 @@ const bool was_already_paused = paused_ || playback_rate_ == 0; paused_ = true; - pipeline_.SetPlaybackRate(0.0f); + pipeline_.SetPlaybackRate(0.0); if (data_source_) data_source_->MediaIsPaused(); UpdatePausedTime();
diff --git a/media/cdm/cenc_utils.cc b/media/cdm/cenc_utils.cc index 2eeafd7..a59c19c 100644 --- a/media/cdm/cenc_utils.cc +++ b/media/cdm/cenc_utils.cc
@@ -70,6 +70,52 @@ return true; } +// Checks that |reader| contains a valid 'ppsh' box header. |reader| is updated +// to point to the content immediately following the box header. Returns true +// if the header looks valid and |reader| contains enough data for the size of +// header. |size| is updated as the computed size of the box header. Otherwise +// false is returned. +static bool ValidBoxHeader(BitReader* reader, uint32* size) { + // Enough data for a miniumum size 'pssh' box? + uint32 available_bytes = reader->bits_available() / 8; + RCHECK(available_bytes >= kMinimumBoxSizeInBytes); + + *size = ReadBits(reader, 32); + + // Must be a 'pssh' box or else fail. + RCHECK(ReadBits(reader, 8) == 'p'); + RCHECK(ReadBits(reader, 8) == 's'); + RCHECK(ReadBits(reader, 8) == 's'); + RCHECK(ReadBits(reader, 8) == 'h'); + + if (*size == 1) { + // If largesize > 2**32 it is too big. + RCHECK(ReadBits(reader, 32) == 0); + *size = ReadBits(reader, 32); + } else if (*size == 0) { + *size = available_bytes; + } + + // Check that the buffer contains at least size bytes. + return available_bytes >= *size; +} + +bool ValidatePsshInput(const uint8* input, size_t input_length) { + size_t offset = 0; + while (offset < input_length) { + // Create a BitReader over the remaining part of the buffer. + BitReader reader(input + offset, input_length - offset); + uint32 size; + RCHECK(ValidBoxHeader(&reader, &size)); + + // Update offset to point at the next 'pssh' box (may not be one). + offset += size; + } + + // Only valid if this contains 0 or more 'pssh' boxes. + return offset == input_length; +} + bool GetKeyIdsForCommonSystemId(const uint8* input, int input_length, std::vector<std::vector<uint8>>* key_ids) { @@ -77,29 +123,10 @@ std::vector<std::vector<uint8>> result; while (offset < input_length) { + // Create a BitReader over the remaining part of the buffer. BitReader reader(input + offset, input_length - offset); - - // Enough data for a miniumum size 'pssh' box? - RCHECK(reader.bits_available() >= kMinimumBoxSizeInBytes * 8); - - uint32 size = ReadBits(&reader, 32); - - // Must be a 'pssh' box or else fail. - RCHECK(ReadBits(&reader, 8) == 'p'); - RCHECK(ReadBits(&reader, 8) == 's'); - RCHECK(ReadBits(&reader, 8) == 's'); - RCHECK(ReadBits(&reader, 8) == 'h'); - - if (size == 1) { - // If largesize > 2**32 it is too big. - RCHECK(ReadBits(&reader, 32) == 0); - size = ReadBits(&reader, 32); - } else if (size == 0) { - size = input_length - offset; - } - - // Check that the buffer contains at least size bytes. - RCHECK(static_cast<uint32>(input_length - offset) >= size); + uint32 size; + RCHECK(ValidBoxHeader(&reader, &size)); // Update offset to point at the next 'pssh' box (may not be one). offset += size;
diff --git a/media/cdm/cenc_utils.h b/media/cdm/cenc_utils.h index c659742..6fd9952 100644 --- a/media/cdm/cenc_utils.h +++ b/media/cdm/cenc_utils.h
@@ -12,6 +12,10 @@ namespace media { +// Validate that |input| is a set of concatenated 'pssh' boxes and the sizes +// match. Returns true if |input| looks valid, false otherwise. +MEDIA_EXPORT bool ValidatePsshInput(const uint8* input, size_t input_length); + // Gets the Key Ids from a 'pssh' box for the Common SystemID among one or // more concatenated 'pssh' boxes. If |input| looks valid, then true is // returned and |key_ids| is updated to contain the values found. Otherwise
diff --git a/media/cdm/cenc_utils_unittest.cc b/media/cdm/cenc_utils_unittest.cc index ae75817..fd481da4 100644 --- a/media/cdm/cenc_utils_unittest.cc +++ b/media/cdm/cenc_utils_unittest.cc
@@ -168,6 +168,7 @@ TEST_F(CencUtilsTest, EmptyPSSH) { std::vector<std::vector<uint8>> key_ids; + EXPECT_TRUE(ValidatePsshInput(nullptr, 0)); EXPECT_TRUE(GetKeyIdsForCommonSystemId(nullptr, 0, &key_ids)); EXPECT_EQ(0u, key_ids.size()); } @@ -175,6 +176,7 @@ TEST_F(CencUtilsTest, PSSHVersion0) { std::vector<uint8> box = MakePSSHBox(0); std::vector<std::vector<uint8>> key_ids; + EXPECT_TRUE(ValidatePsshInput(&box[0], box.size())); EXPECT_TRUE(GetKeyIdsForCommonSystemId(&box[0], box.size(), &key_ids)); EXPECT_EQ(0u, key_ids.size()); } @@ -182,6 +184,7 @@ TEST_F(CencUtilsTest, PSSHVersion1WithNoKeys) { std::vector<uint8> box = MakePSSHBox(1); std::vector<std::vector<uint8>> key_ids; + EXPECT_TRUE(ValidatePsshInput(&box[0], box.size())); EXPECT_TRUE(GetKeyIdsForCommonSystemId(&box[0], box.size(), &key_ids)); EXPECT_EQ(0u, key_ids.size()); } @@ -189,6 +192,7 @@ TEST_F(CencUtilsTest, PSSHVersion1WithOneKey) { std::vector<uint8> box = MakePSSHBox(1, Key1()); std::vector<std::vector<uint8>> key_ids; + EXPECT_TRUE(ValidatePsshInput(&box[0], box.size())); EXPECT_TRUE(GetKeyIdsForCommonSystemId(&box[0], box.size(), &key_ids)); EXPECT_EQ(1u, key_ids.size()); EXPECT_EQ(key_ids[0], Key1()); @@ -197,6 +201,7 @@ TEST_F(CencUtilsTest, PSSHVersion1WithTwoKeys) { std::vector<uint8> box = MakePSSHBox(1, Key1(), Key2()); std::vector<std::vector<uint8>> key_ids; + EXPECT_TRUE(ValidatePsshInput(&box[0], box.size())); EXPECT_TRUE(GetKeyIdsForCommonSystemId(&box[0], box.size(), &key_ids)); EXPECT_EQ(2u, key_ids.size()); EXPECT_EQ(key_ids[0], Key1()); @@ -212,6 +217,7 @@ box0.push_back(value); std::vector<std::vector<uint8>> key_ids; + EXPECT_TRUE(ValidatePsshInput(&box0[0], box0.size())); EXPECT_TRUE(GetKeyIdsForCommonSystemId(&box0[0], box0.size(), &key_ids)); EXPECT_EQ(1u, key_ids.size()); EXPECT_EQ(key_ids[0], Key1()); @@ -226,6 +232,7 @@ box1.push_back(value); std::vector<std::vector<uint8>> key_ids; + EXPECT_TRUE(ValidatePsshInput(&box1[0], box1.size())); EXPECT_TRUE(GetKeyIdsForCommonSystemId(&box1[0], box1.size(), &key_ids)); EXPECT_EQ(1u, key_ids.size()); EXPECT_EQ(key_ids[0], Key1()); @@ -244,6 +251,7 @@ box.push_back(value); std::vector<std::vector<uint8>> key_ids; + EXPECT_TRUE(ValidatePsshInput(&box[0], box.size())); EXPECT_TRUE(GetKeyIdsForCommonSystemId(&box[0], box.size(), &key_ids)); EXPECT_EQ(4u, key_ids.size()); EXPECT_EQ(key_ids[0], Key1()); @@ -257,8 +265,10 @@ std::vector<std::vector<uint8>> key_ids; for (uint32 i = 1; i < box.size(); ++i) { // Modify size of data passed to be less than real size. + EXPECT_FALSE(ValidatePsshInput(&box[0], i)); EXPECT_FALSE(GetKeyIdsForCommonSystemId(&box[0], i, &key_ids)); // Modify starting point. + EXPECT_FALSE(ValidatePsshInput(&box[i], box.size() - i)); EXPECT_FALSE(GetKeyIdsForCommonSystemId(&box[i], box.size() - i, &key_ids)); } } @@ -304,6 +314,7 @@ }; std::vector<std::vector<uint8>> key_ids; + EXPECT_TRUE(ValidatePsshInput(data, arraysize(data))); EXPECT_TRUE(GetKeyIdsForCommonSystemId(data, arraysize(data), &key_ids)); EXPECT_EQ(2u, key_ids.size()); } @@ -325,6 +336,7 @@ }; std::vector<std::vector<uint8>> key_ids; + EXPECT_TRUE(ValidatePsshInput(data, arraysize(data))); EXPECT_TRUE(GetKeyIdsForCommonSystemId(data, arraysize(data), &key_ids)); EXPECT_EQ(2u, key_ids.size()); } @@ -347,6 +359,7 @@ }; std::vector<std::vector<uint8>> key_ids; + EXPECT_FALSE(ValidatePsshInput(data, arraysize(data))); EXPECT_FALSE(GetKeyIdsForCommonSystemId(data, arraysize(data), &key_ids)); }
diff --git a/media/cdm/json_web_key.cc b/media/cdm/json_web_key.cc index 43533f0..f60c6f5 100644 --- a/media/cdm/json_web_key.cc +++ b/media/cdm/json_web_key.cc
@@ -329,6 +329,25 @@ license->swap(result); } +void CreateKeyIdsInitData(const KeyIdList& key_ids, + std::vector<uint8>* init_data) { + // Create the init_data. + scoped_ptr<base::DictionaryValue> dictionary(new base::DictionaryValue()); + scoped_ptr<base::ListValue> list(new base::ListValue()); + for (const auto& key_id : key_ids) + list->AppendString(EncodeBase64Url(&key_id[0], key_id.size())); + dictionary->Set(kKeyIdsTag, list.release()); + + // Serialize the dictionary as a string. + std::string json; + JSONStringValueSerializer serializer(&json); + serializer.Serialize(*dictionary); + + // Convert the serialized data into std::vector and return it. + std::vector<uint8> result(json.begin(), json.end()); + init_data->swap(result); +} + bool ExtractFirstKeyIdFromLicenseRequest(const std::vector<uint8>& license, std::vector<uint8>* first_key) { const std::string license_as_str(
diff --git a/media/cdm/json_web_key.h b/media/cdm/json_web_key.h index 7be4473..c77a814c 100644 --- a/media/cdm/json_web_key.h +++ b/media/cdm/json_web_key.h
@@ -81,6 +81,11 @@ MediaKeys::SessionType session_type, std::vector<uint8>* license); +// Creates a keyIDs init_data message for the |key_ids| specified. +// |key_ids_init_data| is updated to contain the resulting JSON string. +MEDIA_EXPORT void CreateKeyIdsInitData(const KeyIdList& key_ids, + std::vector<uint8>* key_ids_init_data); + // Extract the first key from the license request message. Returns true if // |license| is a valid license request and contains at least one key, // otherwise false and |first_key| is not touched.
diff --git a/media/cdm/json_web_key_unittest.cc b/media/cdm/json_web_key_unittest.cc index ecd9d2a..82b3bb0 100644 --- a/media/cdm/json_web_key_unittest.cc +++ b/media/cdm/json_web_key_unittest.cc
@@ -576,5 +576,34 @@ "'kids'[1] is not valid base64url encoded. Value: AQI/"); } +TEST_F(JSONWebKeyTest, CreateInitData) { + const uint8 data1[] = { 0x01, 0x02 }; + const uint8 data2[] = { 0x01, 0x02, 0x03, 0x04 }; + const uint8 data3[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10 }; + + KeyIdList key_ids; + std::string error_message; + + key_ids.push_back(std::vector<uint8>(data1, data1 + arraysize(data1))); + std::vector<uint8> init_data1; + CreateKeyIdsInitData(key_ids, &init_data1); + std::string result1(init_data1.begin(), init_data1.end()); + EXPECT_EQ(result1, "{\"kids\":[\"AQI\"]}"); + + key_ids.push_back(std::vector<uint8>(data2, data2 + arraysize(data2))); + std::vector<uint8> init_data2; + CreateKeyIdsInitData(key_ids, &init_data2); + std::string result2(init_data2.begin(), init_data2.end()); + EXPECT_EQ(result2, "{\"kids\":[\"AQI\",\"AQIDBA\"]}"); + + key_ids.push_back(std::vector<uint8>(data3, data3 + arraysize(data3))); + std::vector<uint8> init_data3; + CreateKeyIdsInitData(key_ids, &init_data3); + std::string result3(init_data3.begin(), init_data3.end()); + EXPECT_EQ(result3, + "{\"kids\":[\"AQI\",\"AQIDBA\",\"AQIDBAUGBwgJCgsMDQ4PEA\"]}"); +} + } // namespace media
diff --git a/media/filters/audio_clock_unittest.cc b/media/filters/audio_clock_unittest.cc index 7b4d85fd..3fe437e 100644 --- a/media/filters/audio_clock_unittest.cc +++ b/media/filters/audio_clock_unittest.cc
@@ -19,7 +19,7 @@ void WroteAudio(int frames_written, int frames_requested, int delay_frames, - float playback_rate) { + double playback_rate) { clock_.WroteAudio( frames_written, frames_requested, delay_frames, playback_rate); }
diff --git a/media/filters/audio_renderer_algorithm.cc b/media/filters/audio_renderer_algorithm.cc index 16e903c..0d59d398 100644 --- a/media/filters/audio_renderer_algorithm.cc +++ b/media/filters/audio_renderer_algorithm.cc
@@ -49,8 +49,8 @@ // Max/min supported playback rates for fast/slow audio. Audio outside of these // ranges are muted. // Audio at these speeds would sound better under a frequency domain algorithm. -static const float kMinPlaybackRate = 0.5f; -static const float kMaxPlaybackRate = 4.0f; +static const double kMinPlaybackRate = 0.5; +static const double kMaxPlaybackRate = 4.0; // Overlap-and-add window size in milliseconds. static const int kOlaWindowSizeMs = 20; @@ -144,7 +144,7 @@ int AudioRendererAlgorithm::FillBuffer(AudioBus* dest, int dest_offset, int requested_frames, - float playback_rate) { + double playback_rate) { if (playback_rate == 0) return 0; @@ -237,7 +237,7 @@ search_block_index_ + search_block_size <= frames; } -bool AudioRendererAlgorithm::RunOneWsolaIteration(float playback_rate) { +bool AudioRendererAlgorithm::RunOneWsolaIteration(double playback_rate) { if (!CanPerformWsola()) return false; @@ -263,7 +263,7 @@ return true; } -void AudioRendererAlgorithm::UpdateOutputTime(float playback_rate, +void AudioRendererAlgorithm::UpdateOutputTime(double playback_rate, double time_change) { output_time_ += time_change; // Center of the search region, in frames. @@ -272,7 +272,7 @@ search_block_index_ = search_block_center_index - search_block_center_offset_; } -void AudioRendererAlgorithm::RemoveOldInputFrames(float playback_rate) { +void AudioRendererAlgorithm::RemoveOldInputFrames(double playback_rate) { const int earliest_used_index = std::min(target_block_index_, search_block_index_); if (earliest_used_index <= 0)
diff --git a/media/filters/audio_renderer_algorithm.h b/media/filters/audio_renderer_algorithm.h index 25ac811..2005bfe 100644 --- a/media/filters/audio_renderer_algorithm.h +++ b/media/filters/audio_renderer_algorithm.h
@@ -51,7 +51,7 @@ int FillBuffer(AudioBus* dest, int dest_offset, int requested_frames, - float playback_rate); + double playback_rate); // Clears |audio_buffer_|. void FlushBuffers(); @@ -101,15 +101,15 @@ // Run one iteration of WSOLA, if there are sufficient frames. This will // overlap-and-add one block to |wsola_output_|, hence, |num_complete_frames_| // is incremented by |ola_hop_size_|. - bool RunOneWsolaIteration(float playback_rate); + bool RunOneWsolaIteration(double playback_rate); // Seek |audio_buffer_| forward to remove frames from input that are not used // any more. State of the WSOLA will be updated accordingly. - void RemoveOldInputFrames(float playback_rate); + void RemoveOldInputFrames(double playback_rate); // Update |output_time_| by |time_change|. In turn |search_block_index_| is // updated. - void UpdateOutputTime(float playback_rate, double time_change); + void UpdateOutputTime(double playback_rate, double time_change); // Is |target_block_| fully within |search_block_|? If so, we don't need to // perform the search.
diff --git a/media/filters/audio_renderer_algorithm_unittest.cc b/media/filters/audio_renderer_algorithm_unittest.cc index 47ce30e..003cd512 100644 --- a/media/filters/audio_renderer_algorithm_unittest.cc +++ b/media/filters/audio_renderer_algorithm_unittest.cc
@@ -239,7 +239,7 @@ EXPECT_NEAR(playback_rate, actual_playback_rate, playback_rate / 100.0); } - void WsolaTest(float playback_rate) { + void WsolaTest(double playback_rate) { const int kSampleRateHz = 48000; const ChannelLayout kChannelLayout = CHANNEL_LAYOUT_STEREO; const int kBytesPerSample = 2; @@ -640,11 +640,11 @@ } TEST_F(AudioRendererAlgorithmTest, WsolaSlowdown) { - WsolaTest(0.6f); + WsolaTest(0.6); } TEST_F(AudioRendererAlgorithmTest, WsolaSpeedup) { - WsolaTest(1.6f); + WsolaTest(1.6); } TEST_F(AudioRendererAlgorithmTest, FillBufferOffset) {
diff --git a/media/media_nacl.gyp b/media/media_nacl.gyp index 39c832e..9697a8a 100644 --- a/media/media_nacl.gyp +++ b/media/media_nacl.gyp
@@ -27,7 +27,6 @@ 'dependencies': [ '../base/base_nacl.gyp:base_nacl', '../base/base_nacl.gyp:base_nacl_nonsfi', - '../native_client/tools.gyp:prep_toolchain', ], 'defines': [ 'MEDIA_IMPLEMENTATION', @@ -51,9 +50,6 @@ 'build_newlib': 0, 'build_pnacl_newlib': 1, }, - 'dependencies': [ - '../native_client/tools.gyp:prep_toolchain', - ], 'sources': [ 'base/media.cc', 'base/media.h',
diff --git a/media/media_options.gni b/media/media_options.gni index 9ac7c5e..746b541 100644 --- a/media/media_options.gni +++ b/media/media_options.gni
@@ -45,10 +45,6 @@ # default since it's not available on the normal Web Platform and costs money. enable_mpeg2ts_stream_parser = false - # Enables browser side Content Decryption Modules. Required for embedders - # (e.g. Android and ChromeCast) that use a browser side CDM. - enable_browser_cdms = is_android - # Experiment to enable mojo based media renderer: http://crbug.com/431776 enable_media_mojo_renderer = false
diff --git a/media/midi/midi_manager_win.h b/media/midi/midi_manager_win.h index 9cb8fb0b..73c1fe5 100644 --- a/media/midi/midi_manager_win.h +++ b/media/midi/midi_manager_win.h
@@ -42,7 +42,7 @@ class MidiManagerWin final : public MidiManager, public MidiServiceWinDelegate { public: MidiManagerWin() {} - virtual ~MidiManagerWin() { midi_service_.reset(); } + ~MidiManagerWin() override { midi_service_.reset(); } // MidiManager overrides: void StartInitialization() final;
diff --git a/media/mojo/interfaces/media_renderer.mojom b/media/mojo/interfaces/media_renderer.mojom index 9165a33..616e677 100644 --- a/media/mojo/interfaces/media_renderer.mojom +++ b/media/mojo/interfaces/media_renderer.mojom
@@ -25,7 +25,7 @@ StartPlayingFrom(int64 time_usec); // Updates the current playback rate. The default playback rate should be 1. - SetPlaybackRate(float playback_rate); + SetPlaybackRate(double playback_rate); // Sets the output volume. The default volume should be 1. SetVolume(float volume);
diff --git a/media/mojo/services/mojo_renderer_impl.cc b/media/mojo/services/mojo_renderer_impl.cc index d1b41a6..73e677d 100644 --- a/media/mojo/services/mojo_renderer_impl.cc +++ b/media/mojo/services/mojo_renderer_impl.cc
@@ -103,7 +103,7 @@ remote_media_renderer_->StartPlayingFrom(time.InMicroseconds()); } -void MojoRendererImpl::SetPlaybackRate(float playback_rate) { +void MojoRendererImpl::SetPlaybackRate(double playback_rate) { DVLOG(2) << __FUNCTION__; DCHECK(task_runner_->BelongsToCurrentThread()); remote_media_renderer_->SetPlaybackRate(playback_rate);
diff --git a/media/mojo/services/mojo_renderer_impl.h b/media/mojo/services/mojo_renderer_impl.h index cea3284..7b955a2c 100644 --- a/media/mojo/services/mojo_renderer_impl.h +++ b/media/mojo/services/mojo_renderer_impl.h
@@ -44,7 +44,7 @@ const CdmAttachedCB& cdm_attached_cb) override; void Flush(const base::Closure& flush_cb) override; void StartPlayingFrom(base::TimeDelta time) override; - void SetPlaybackRate(float playback_rate) override; + void SetPlaybackRate(double playback_rate) override; void SetVolume(float volume) override; base::TimeDelta GetMediaTime() override; bool HasAudio() override;
diff --git a/media/mojo/services/mojo_renderer_service.cc b/media/mojo/services/mojo_renderer_service.cc index 0601c2c4..ba81a212 100644 --- a/media/mojo/services/mojo_renderer_service.cc +++ b/media/mojo/services/mojo_renderer_service.cc
@@ -93,7 +93,7 @@ SchedulePeriodicMediaTimeUpdates(); } -void MojoRendererService::SetPlaybackRate(float playback_rate) { +void MojoRendererService::SetPlaybackRate(double playback_rate) { DVLOG(2) << __FUNCTION__ << ": " << playback_rate; DCHECK_EQ(state_, STATE_PLAYING); renderer_->SetPlaybackRate(playback_rate);
diff --git a/media/mojo/services/mojo_renderer_service.h b/media/mojo/services/mojo_renderer_service.h index 6003515..1b81168 100644 --- a/media/mojo/services/mojo_renderer_service.h +++ b/media/mojo/services/mojo_renderer_service.h
@@ -44,7 +44,7 @@ const mojo::Closure& callback) override; void Flush(const mojo::Closure& callback) override; void StartPlayingFrom(int64_t time_delta_usec) override; - void SetPlaybackRate(float playback_rate) override; + void SetPlaybackRate(double playback_rate) override; void SetVolume(float volume) override; private:
diff --git a/media/renderers/audio_renderer_impl.cc b/media/renderers/audio_renderer_impl.cc index 6f839db..dbc1b8e 100644 --- a/media/renderers/audio_renderer_impl.cc +++ b/media/renderers/audio_renderer_impl.cc
@@ -52,7 +52,7 @@ audio_buffer_stream_( new AudioBufferStream(task_runner, decoders.Pass(), media_log)), hardware_config_(hardware_config), - playback_rate_(0), + playback_rate_(0.0), state_(kUninitialized), buffering_state_(BUFFERING_HAVE_NOTHING), rendering_(false), @@ -100,7 +100,7 @@ DCHECK(task_runner_->BelongsToCurrentThread()); DCHECK_EQ(state_, kPlaying); DCHECK(!sink_playing_); - DCHECK_NE(playback_rate_, 0); + DCHECK_NE(playback_rate_, 0.0); lock_.AssertAcquired(); sink_playing_ = true; @@ -535,7 +535,7 @@ !algorithm_->IsQueueFull(); } -void AudioRendererImpl::SetPlaybackRate(float playback_rate) { +void AudioRendererImpl::SetPlaybackRate(double playback_rate) { DVLOG(1) << __FUNCTION__ << "(" << playback_rate << ")"; DCHECK(task_runner_->BelongsToCurrentThread()); DCHECK_GE(playback_rate, 0); @@ -546,7 +546,7 @@ // We have two cases here: // Play: current_playback_rate == 0 && playback_rate != 0 // Pause: current_playback_rate != 0 && playback_rate == 0 - float current_playback_rate = playback_rate_; + double current_playback_rate = playback_rate_; playback_rate_ = playback_rate; if (!rendering_)
diff --git a/media/renderers/audio_renderer_impl.h b/media/renderers/audio_renderer_impl.h index 366b492..bd11f40 100644 --- a/media/renderers/audio_renderer_impl.h +++ b/media/renderers/audio_renderer_impl.h
@@ -68,7 +68,7 @@ // TimeSource implementation. void StartTicking() override; void StopTicking() override; - void SetPlaybackRate(float rate) override; + void SetPlaybackRate(double rate) override; void SetMediaTime(base::TimeDelta time) override; base::TimeDelta CurrentMediaTime() override; base::TimeTicks GetWallClockTime(base::TimeDelta time) override;
diff --git a/media/renderers/audio_renderer_impl_unittest.cc b/media/renderers/audio_renderer_impl_unittest.cc index 98782c93..1fe5bdd 100644 --- a/media/renderers/audio_renderer_impl_unittest.cc +++ b/media/renderers/audio_renderer_impl_unittest.cc
@@ -212,7 +212,7 @@ void StartTicking() { renderer_->StartTicking(); - renderer_->SetPlaybackRate(1.0f); + renderer_->SetPlaybackRate(1.0); } void StopTicking() { renderer_->StopTicking(); } @@ -721,18 +721,18 @@ // Rendering hasn't started. Sink should always be paused. EXPECT_EQ(FakeAudioRendererSink::kPaused, sink_->state()); - renderer_->SetPlaybackRate(0.0f); + renderer_->SetPlaybackRate(0.0); EXPECT_EQ(FakeAudioRendererSink::kPaused, sink_->state()); - renderer_->SetPlaybackRate(1.0f); + renderer_->SetPlaybackRate(1.0); EXPECT_EQ(FakeAudioRendererSink::kPaused, sink_->state()); // Rendering has started with non-zero rate. Rate changes will affect sink // state. renderer_->StartTicking(); EXPECT_EQ(FakeAudioRendererSink::kPlaying, sink_->state()); - renderer_->SetPlaybackRate(0.0f); + renderer_->SetPlaybackRate(0.0); EXPECT_EQ(FakeAudioRendererSink::kPaused, sink_->state()); - renderer_->SetPlaybackRate(1.0f); + renderer_->SetPlaybackRate(1.0); EXPECT_EQ(FakeAudioRendererSink::kPlaying, sink_->state()); // Rendering has stopped. Sink should be paused. @@ -741,10 +741,10 @@ // Start rendering with zero playback rate. Sink should be paused until // non-zero rate is set. - renderer_->SetPlaybackRate(0.0f); + renderer_->SetPlaybackRate(0.0); renderer_->StartTicking(); EXPECT_EQ(FakeAudioRendererSink::kPaused, sink_->state()); - renderer_->SetPlaybackRate(1.0f); + renderer_->SetPlaybackRate(1.0); EXPECT_EQ(FakeAudioRendererSink::kPlaying, sink_->state()); }
diff --git a/media/renderers/renderer_impl.cc b/media/renderers/renderer_impl.cc index 62caf9d8..30eeb4f 100644 --- a/media/renderers/renderer_impl.cc +++ b/media/renderers/renderer_impl.cc
@@ -35,7 +35,7 @@ video_renderer_(video_renderer.Pass()), time_source_(NULL), time_ticking_(false), - playback_rate_(0), + playback_rate_(0.0), audio_buffering_state_(BUFFERING_HAVE_NOTHING), video_buffering_state_(BUFFERING_HAVE_NOTHING), audio_ended_(false), @@ -166,7 +166,7 @@ video_renderer_->StartPlayingFrom(time); } -void RendererImpl::SetPlaybackRate(float playback_rate) { +void RendererImpl::SetPlaybackRate(double playback_rate) { DVLOG(1) << __FUNCTION__ << "(" << playback_rate << ")"; DCHECK(task_runner_->BelongsToCurrentThread()); @@ -176,7 +176,7 @@ time_source_->SetPlaybackRate(playback_rate); - const float old_rate = playback_rate_; + const double old_rate = playback_rate_; playback_rate_ = playback_rate; if (!time_ticking_ || !video_renderer_) return;
diff --git a/media/renderers/renderer_impl.h b/media/renderers/renderer_impl.h index 3f90271c..79118488 100644 --- a/media/renderers/renderer_impl.h +++ b/media/renderers/renderer_impl.h
@@ -55,7 +55,7 @@ const CdmAttachedCB& cdm_attached_cb) final; void Flush(const base::Closure& flush_cb) final; void StartPlayingFrom(base::TimeDelta time) final; - void SetPlaybackRate(float playback_rate) final; + void SetPlaybackRate(double playback_rate) final; void SetVolume(float volume) final; base::TimeDelta GetMediaTime() final; bool HasAudio() final; @@ -152,7 +152,7 @@ TimeSource* time_source_; scoped_ptr<WallClockTimeSource> wall_clock_time_source_; bool time_ticking_; - float playback_rate_; + double playback_rate_; // The time to start playback from after starting/seeking has completed. base::TimeDelta start_time_;
diff --git a/media/renderers/renderer_impl_unittest.cc b/media/renderers/renderer_impl_unittest.cc index 9fcefe4..df4c8cc 100644 --- a/media/renderers/renderer_impl_unittest.cc +++ b/media/renderers/renderer_impl_unittest.cc
@@ -217,7 +217,7 @@ base::RunLoop().RunUntilIdle(); } - void SetPlaybackRate(float playback_rate) { + void SetPlaybackRate(double playback_rate) { EXPECT_CALL(time_source_, SetPlaybackRate(playback_rate)); renderer_impl_->SetPlaybackRate(playback_rate); base::RunLoop().RunUntilIdle(); @@ -227,7 +227,7 @@ return renderer_impl_->GetMediaTime().InMilliseconds(); } - bool IsMediaTimeAdvancing(float playback_rate) { + bool IsMediaTimeAdvancing(double playback_rate) { int64 start_time_ms = GetMediaTimeMs(); const int64 time_to_advance_ms = 100; @@ -242,7 +242,7 @@ } bool IsMediaTimeAdvancing() { - return IsMediaTimeAdvancing(1.0f); + return IsMediaTimeAdvancing(1.0); } // Fixture members. @@ -377,8 +377,8 @@ TEST_F(RendererImplTest, SetPlaybackRate) { InitializeWithAudioAndVideo(); - SetPlaybackRate(1.0f); - SetPlaybackRate(2.0f); + SetPlaybackRate(1.0); + SetPlaybackRate(2.0); } TEST_F(RendererImplTest, SetVolume) {
diff --git a/media/video/capture/fake_video_capture_device.cc b/media/video/capture/fake_video_capture_device.cc index 4f3a8bb..a2cd37d 100644 --- a/media/video/capture/fake_video_capture_device.cc +++ b/media/video/capture/fake_video_capture_device.cc
@@ -101,26 +101,25 @@ else capture_format_.frame_size.SetSize(320, 240); - switch (device_type_) { - case USING_OWN_BUFFERS: - fake_frame_.reset(new uint8[VideoFrame::AllocationSize( - VideoFrame::I420, capture_format_.frame_size)]); - BeepAndScheduleNextCapture( - base::Bind(&FakeVideoCaptureDevice::CaptureUsingOwnBuffers, - weak_factory_.GetWeakPtr())); - break; - case USING_CLIENT_BUFFERS: - BeepAndScheduleNextCapture( - base::Bind(&FakeVideoCaptureDevice::CaptureUsingClientBuffers, - weak_factory_.GetWeakPtr())); - break; - case USING_GPU_MEMORY_BUFFERS: - BeepAndScheduleNextCapture( - base::Bind(&FakeVideoCaptureDevice::CaptureUsingGpuMemoryBuffers, - weak_factory_.GetWeakPtr())); - break; - default: - client_->OnError("Unknown Fake Video Capture Device type."); + if (device_type_ == USING_OWN_BUFFERS || + device_type_ == USING_OWN_BUFFERS_TRIPLANAR) { + fake_frame_.reset(new uint8[VideoFrame::AllocationSize( + VideoFrame::I420, capture_format_.frame_size)]); + BeepAndScheduleNextCapture( + base::Bind(&FakeVideoCaptureDevice::CaptureUsingOwnBuffers, + weak_factory_.GetWeakPtr())); + } else if (device_type_ == USING_CLIENT_BUFFERS_I420 || + device_type_ == USING_CLIENT_BUFFERS_GPU) { + DVLOG(1) << "starting with " << (device_type_ == USING_CLIENT_BUFFERS_I420 + ? "Client buffers" + : "GpuMemoryBuffers"); + BeepAndScheduleNextCapture(base::Bind( + &FakeVideoCaptureDevice::CaptureUsingClientBuffers, + weak_factory_.GetWeakPtr(), (device_type_ == USING_CLIENT_BUFFERS_I420 + ? PIXEL_FORMAT_I420 + : PIXEL_FORMAT_GPUMEMORYBUFFER))); + } else { + client_->OnError("Unknown Fake Video Capture Device type."); } } @@ -131,8 +130,7 @@ void FakeVideoCaptureDevice::CaptureUsingOwnBuffers() { DCHECK(thread_checker_.CalledOnValidThread()); - const size_t frame_size = - VideoFrame::AllocationSize(VideoFrame::I420, capture_format_.frame_size); + const size_t frame_size = capture_format_.ImageAllocationSize(); memset(fake_frame_.get(), 0, frame_size); DrawPacman(false /* use_argb */, @@ -142,62 +140,60 @@ capture_format_.frame_size); // Give the captured frame to the client. - client_->OnIncomingCapturedData(fake_frame_.get(), - frame_size, - capture_format_, - 0, - base::TimeTicks::Now()); + if (device_type_ == USING_OWN_BUFFERS) { + client_->OnIncomingCapturedData(fake_frame_.get(), + frame_size, + capture_format_, + 0 /* rotation */, + base::TimeTicks::Now()); + } else if (device_type_ == USING_OWN_BUFFERS_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()); + } BeepAndScheduleNextCapture( base::Bind(&FakeVideoCaptureDevice::CaptureUsingOwnBuffers, weak_factory_.GetWeakPtr())); } -void FakeVideoCaptureDevice::CaptureUsingClientBuffers() { +void FakeVideoCaptureDevice::CaptureUsingClientBuffers( + VideoPixelFormat pixel_format) { DCHECK(thread_checker_.CalledOnValidThread()); - const scoped_refptr<VideoCaptureDevice::Client::Buffer> capture_buffer = - client_->ReserveOutputBuffer(capture_format_.pixel_format, - capture_format_.frame_size); + scoped_ptr<VideoCaptureDevice::Client::Buffer> capture_buffer( + client_->ReserveOutputBuffer(pixel_format, capture_format_.frame_size)); DLOG_IF(ERROR, !capture_buffer) << "Couldn't allocate Capture Buffer"; - if (!capture_buffer) - return; - uint8_t* const data_ptr = static_cast<uint8_t*>(capture_buffer->data()); - memset(data_ptr, 0, capture_buffer->size()); - DCHECK(data_ptr) << "Buffer has NO backing memory"; + if (capture_buffer.get()) { + uint8_t* const data_ptr = static_cast<uint8_t*>(capture_buffer->data()); + DCHECK(data_ptr) << "Buffer has NO backing memory"; + memset(data_ptr, 0, capture_buffer->size()); - DrawPacman(false /* use_argb */, - data_ptr, - frame_count_, - kFakeCapturePeriodMs, - capture_format_.frame_size); + DrawPacman( + (pixel_format == media::PIXEL_FORMAT_GPUMEMORYBUFFER), /* use_argb */ + data_ptr, + frame_count_, + kFakeCapturePeriodMs, + capture_format_.frame_size); - scoped_refptr<VideoFrame> video_frame = - VideoFrame::WrapExternalPackedMemory( - VideoFrame::I420, - capture_format_.frame_size, - gfx::Rect(capture_format_.frame_size), - capture_format_.frame_size, - static_cast<uint8*>(capture_buffer->data()), - capture_buffer->size(), - base::SharedMemory::NULLHandle(), - 0, - base::TimeDelta(), - base::Closure()); + // Give the captured frame to the client. + const VideoCaptureFormat format(capture_format_.frame_size, + capture_format_.frame_rate, + pixel_format); + client_->OnIncomingCapturedBuffer(capture_buffer.Pass(), format, + base::TimeTicks::Now()); + } - // Give the captured frame to the client. - client_->OnIncomingCapturedVideoFrame(capture_buffer, - video_frame, - base::TimeTicks::Now()); BeepAndScheduleNextCapture( base::Bind(&FakeVideoCaptureDevice::CaptureUsingClientBuffers, - weak_factory_.GetWeakPtr())); -} - -void FakeVideoCaptureDevice::CaptureUsingGpuMemoryBuffers() { - DCHECK(thread_checker_.CalledOnValidThread()); - - NOTIMPLEMENTED(); + weak_factory_.GetWeakPtr(), pixel_format)); } void FakeVideoCaptureDevice::BeepAndScheduleNextCapture(
diff --git a/media/video/capture/fake_video_capture_device.h b/media/video/capture/fake_video_capture_device.h index e785ce6..f4a19f9 100644 --- a/media/video/capture/fake_video_capture_device.h +++ b/media/video/capture/fake_video_capture_device.h
@@ -23,8 +23,9 @@ public: enum FakeVideoCaptureDeviceType { USING_OWN_BUFFERS, - USING_CLIENT_BUFFERS, - USING_GPU_MEMORY_BUFFERS, + USING_OWN_BUFFERS_TRIPLANAR, + USING_CLIENT_BUFFERS_I420, + USING_CLIENT_BUFFERS_GPU, }; static int FakeCapturePeriodMs() { return kFakeCapturePeriodMs; } @@ -41,8 +42,7 @@ static const int kFakeCapturePeriodMs = 50; void CaptureUsingOwnBuffers(); - void CaptureUsingClientBuffers(); - void CaptureUsingGpuMemoryBuffers(); + void CaptureUsingClientBuffers(VideoPixelFormat pixel_format); void BeepAndScheduleNextCapture(const base::Closure& next_capture); // |thread_checker_| is used to check that all methods are called in the
diff --git a/media/video/capture/fake_video_capture_device_factory.cc b/media/video/capture/fake_video_capture_device_factory.cc index aea1909..76de761 100644 --- a/media/video/capture/fake_video_capture_device_factory.cc +++ b/media/video/capture/fake_video_capture_device_factory.cc
@@ -26,10 +26,12 @@ FakeVideoCaptureDevice::FakeVideoCaptureDeviceType fake_vcd_type; if (option.empty()) fake_vcd_type = FakeVideoCaptureDevice::USING_OWN_BUFFERS; + else if (base:: strcasecmp(option.c_str(), "triplanar") == 0) + fake_vcd_type = FakeVideoCaptureDevice::USING_OWN_BUFFERS_TRIPLANAR; else if (base:: strcasecmp(option.c_str(), "gpu") == 0) - fake_vcd_type = FakeVideoCaptureDevice::USING_GPU_MEMORY_BUFFERS; + fake_vcd_type = FakeVideoCaptureDevice::USING_CLIENT_BUFFERS_GPU; else - fake_vcd_type = FakeVideoCaptureDevice::USING_CLIENT_BUFFERS; + fake_vcd_type = FakeVideoCaptureDevice::USING_CLIENT_BUFFERS_I420; for (int n = 0; n < number_of_devices_; ++n) { std::string possible_id = base::StringPrintf("/dev/video%d", n); @@ -73,9 +75,8 @@ gfx::Size(1920, 1080)}; supported_formats->clear(); for (const auto& size : supported_sizes) { - supported_formats->push_back(VideoCaptureFormat(size, - frame_rate, - media::PIXEL_FORMAT_I420)); + supported_formats->push_back( + VideoCaptureFormat(size, frame_rate, media::PIXEL_FORMAT_I420)); } }
diff --git a/media/video/capture/fake_video_capture_device_unittest.cc b/media/video/capture/fake_video_capture_device_unittest.cc index e413c5ed4..e340ae43 100644 --- a/media/video/capture/fake_video_capture_device_unittest.cc +++ b/media/video/capture/fake_video_capture_device_unittest.cc
@@ -24,25 +24,23 @@ static const FakeVideoCaptureDevice::FakeVideoCaptureDeviceType kCaptureTypes[] = { FakeVideoCaptureDevice::USING_OWN_BUFFERS, - FakeVideoCaptureDevice::USING_CLIENT_BUFFERS, - // TODO(mcasas): Add FakeVideoCaptureDevice::USING_GPU_MEMORY_BUFFERS when - // implemented. + FakeVideoCaptureDevice::USING_OWN_BUFFERS_TRIPLANAR, + FakeVideoCaptureDevice::USING_CLIENT_BUFFERS_I420, + FakeVideoCaptureDevice::USING_CLIENT_BUFFERS_GPU, }; // This class is a Client::Buffer that allocates and frees the requested |size|. class MockBuffer : public VideoCaptureDevice::Client::Buffer { public: MockBuffer(int buffer_id, size_t size) - : id_(buffer_id), - size_(size), - data_(new uint8[size_]) {} + : id_(buffer_id), size_(size), data_(new uint8[size_]) {} + ~MockBuffer() override { delete[] data_; } int id() const override { return id_; } - void* data() const override { return static_cast<void*>(data_); } size_t size() const override { return size_; } + void* data() override { return data_; } + ClientBuffer AsClientBuffer() override { return nullptr; } private: - ~MockBuffer() override { delete[] data_; } - const int id_; const size_t size_; uint8* const data_; @@ -50,22 +48,12 @@ class MockClient : public VideoCaptureDevice::Client { public: - MOCK_METHOD9(OnIncomingCapturedYuvData, - void (const uint8* y_data, - const uint8* u_data, - const uint8* 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)); MOCK_METHOD1(OnError, void(const std::string& reason)); explicit MockClient(base::Callback<void(const VideoCaptureFormat&)> frame_cb) : frame_cb_(frame_cb) {} - // Client virtual method for capturing using Device Buffers. + // Client virtual methods for capturing using Device Buffers. void OnIncomingCapturedData(const uint8* data, int length, const VideoCaptureFormat& format, @@ -73,16 +61,35 @@ const base::TimeTicks& timestamp) { frame_cb_.Run(format); } + void OnIncomingCapturedYuvData(const uint8* y_data, + const uint8* u_data, + const uint8* 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_refptr<Buffer> ReserveOutputBuffer(media::VideoPixelFormat format, - const gfx::Size& dimensions) { - EXPECT_EQ(format, PIXEL_FORMAT_I420); + scoped_ptr<Buffer> ReserveOutputBuffer(media::VideoPixelFormat format, + const gfx::Size& dimensions) { + EXPECT_TRUE(format == PIXEL_FORMAT_I420 || + format == PIXEL_FORMAT_GPUMEMORYBUFFER); EXPECT_GT(dimensions.GetArea(), 0); - return make_scoped_refptr(new MockBuffer(0, dimensions.GetArea() * 3 / 2)); + const VideoCaptureFormat frame_format(dimensions, 0.0, format); + return make_scoped_ptr( + new MockBuffer(0, frame_format.ImageAllocationSize())); + } + void OnIncomingCapturedBuffer(scoped_ptr<Buffer> buffer, + const VideoCaptureFormat& frame_format, + const base::TimeTicks& timestamp) { + frame_cb_.Run(frame_format); } void OnIncomingCapturedVideoFrame( - const scoped_refptr<Buffer>& buffer, + scoped_ptr<Buffer> buffer, const scoped_refptr<media::VideoFrame>& frame, const base::TimeTicks& timestamp) { VideoCaptureFormat format(frame->natural_size(), 30.0, PIXEL_FORMAT_I420); @@ -125,8 +132,6 @@ } void SetUp() override { - EXPECT_CALL(*client_, OnIncomingCapturedYuvData(_,_,_,_,_,_,_,_,_)) - .Times(0); EXPECT_CALL(*client_, OnError(_)).Times(0); }
diff --git a/media/video/capture/video_capture_device.cc b/media/video/capture/video_capture_device.cc index 1eb205a..44442c4 100644 --- a/media/video/capture/video_capture_device.cc +++ b/media/video/capture/video_capture_device.cc
@@ -128,6 +128,8 @@ } #endif +VideoCaptureDevice::Client::Buffer::~Buffer() {} + VideoCaptureDevice::~VideoCaptureDevice() {} int VideoCaptureDevice::GetPowerLineFrequencyForLocation() const {
diff --git a/media/video/capture/video_capture_device.h b/media/video/capture/video_capture_device.h index e5e42f8..d3dfe72 100644 --- a/media/video/capture/video_capture_device.h +++ b/media/video/capture/video_capture_device.h
@@ -23,6 +23,7 @@ #include "media/base/media_export.h" #include "media/base/video_capture_types.h" #include "media/base/video_frame.h" +#include "ui/gfx/gpu_memory_buffer.h" namespace media { @@ -195,15 +196,13 @@ class MEDIA_EXPORT Client { public: // Memory buffer returned by Client::ReserveOutputBuffer(). - class Buffer : public base::RefCountedThreadSafe<Buffer> { + class MEDIA_EXPORT Buffer { public: + virtual ~Buffer() = 0; virtual int id() const = 0; - virtual void* data() const = 0; virtual size_t size() const = 0; - - protected: - friend class base::RefCountedThreadSafe<Buffer>; - virtual ~Buffer() {} + virtual void* data() = 0; + virtual ClientBuffer AsClientBuffer() = 0; }; virtual ~Client() {} @@ -241,20 +240,25 @@ // backing, but functions as a reservation for external input for the // purposes of buffer throttling. // - // The output buffer stays reserved for use until the Buffer object is - // destroyed. - virtual scoped_refptr<Buffer> ReserveOutputBuffer( + // The output buffer stays reserved and mapped for use until the Buffer + // object is destroyed or returned. + virtual scoped_ptr<Buffer> ReserveOutputBuffer( media::VideoPixelFormat format, const gfx::Size& dimensions) = 0; - // Captured a new video frame, held in |frame|. + // Captured new video data, held in |frame| or |buffer|, respectively for + // OnIncomingCapturedVideoFrame() and OnIncomingCapturedBuffer(). // - // As the frame is backed by a reservation returned by + // In both cases, as the frame is backed by a reservation returned by // ReserveOutputBuffer(), delivery is guaranteed and will require no // additional copies in the browser process. + virtual void OnIncomingCapturedBuffer( + scoped_ptr<Buffer> buffer, + const VideoCaptureFormat& frame_format, + const base::TimeTicks& timestamp) = 0; virtual void OnIncomingCapturedVideoFrame( - const scoped_refptr<Buffer>& buffer, - const scoped_refptr<media::VideoFrame>& frame, + scoped_ptr<Buffer> buffer, + const scoped_refptr<VideoFrame>& frame, const base::TimeTicks& timestamp) = 0; // An error has occurred that cannot be handled and VideoCaptureDevice must
diff --git a/media/video/capture/video_capture_device_unittest.cc b/media/video/capture/video_capture_device_unittest.cc index a01fbab9..961957f1 100644 --- a/media/video/capture/video_capture_device_unittest.cc +++ b/media/video/capture/video_capture_device_unittest.cc
@@ -64,23 +64,19 @@ class MockClient : public VideoCaptureDevice::Client { public: - MOCK_METHOD2(ReserveOutputBuffer, - scoped_refptr<Buffer>(media::VideoPixelFormat format, - const gfx::Size& dimensions)); MOCK_METHOD9(OnIncomingCapturedYuvData, - void (const uint8* y_data, - const uint8* u_data, - const uint8* 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)); - MOCK_METHOD3(OnIncomingCapturedVideoFrame, - void(const scoped_refptr<Buffer>& buffer, - const scoped_refptr<VideoFrame>& frame, + void(const uint8* y_data, + const uint8* u_data, + const uint8* 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)); + MOCK_METHOD0(DoReserveOutputBuffer, void(void)); + MOCK_METHOD0(DoOnIncomingCapturedBuffer, void(void)); + MOCK_METHOD0(DoOnIncomingCapturedVideoFrame, void(void)); MOCK_METHOD1(OnError, void(const std::string& reason)); explicit MockClient(base::Callback<void(const VideoCaptureFormat&)> frame_cb) @@ -96,6 +92,23 @@ main_thread_->PostTask(FROM_HERE, base::Bind(frame_cb_, format)); } + // Trampoline methods to workaround GMOCK problems with scoped_ptr<>. + scoped_ptr<Buffer> ReserveOutputBuffer(VideoPixelFormat format, + const gfx::Size& dimensions) override { + DoReserveOutputBuffer(); + return scoped_ptr<Buffer>(); + } + void OnIncomingCapturedBuffer(scoped_ptr<Buffer> buffer, + const VideoCaptureFormat& frame_format, + const base::TimeTicks& timestamp) override { + DoOnIncomingCapturedBuffer(); + } + void OnIncomingCapturedVideoFrame(scoped_ptr<Buffer> buffer, + const scoped_refptr<VideoFrame>& frame, + const base::TimeTicks& timestamp) override { + DoOnIncomingCapturedVideoFrame(); + } + private: scoped_refptr<base::SingleThreadTaskRunner> main_thread_; base::Callback<void(const VideoCaptureFormat&)> frame_cb_; @@ -139,8 +152,9 @@ #endif EXPECT_CALL(*client_, OnIncomingCapturedYuvData(_,_,_,_,_,_,_,_,_)) .Times(0); - EXPECT_CALL(*client_, ReserveOutputBuffer(_,_)).Times(0); - EXPECT_CALL(*client_, OnIncomingCapturedVideoFrame(_,_,_)).Times(0); + EXPECT_CALL(*client_, DoReserveOutputBuffer()).Times(0); + EXPECT_CALL(*client_, DoOnIncomingCapturedBuffer()).Times(0); + EXPECT_CALL(*client_, DoOnIncomingCapturedVideoFrame()).Times(0); } void ResetWithNewClient() {
diff --git a/media/video/capture/win/filter_base_win.cc b/media/video/capture/win/filter_base_win.cc index 96709d5..1c3e24e 100644 --- a/media/video/capture/win/filter_base_win.cc +++ b/media/video/capture/win/filter_base_win.cc
@@ -22,7 +22,7 @@ } // IUnknown implementation. - STDMETHOD(QueryInterface)(REFIID iid, void** object_ptr) { + STDMETHOD(QueryInterface)(REFIID iid, void** object_ptr) override { if (iid == IID_IEnumPins || iid == IID_IUnknown) { AddRef(); *object_ptr = static_cast<IEnumPins*>(this); @@ -31,18 +31,18 @@ return E_NOINTERFACE; } - STDMETHOD_(ULONG, AddRef)() { + STDMETHOD_(ULONG, AddRef)() override { base::RefCounted<PinEnumerator>::AddRef(); return 1; } - STDMETHOD_(ULONG, Release)() { + STDMETHOD_(ULONG, Release)() override { base::RefCounted<PinEnumerator>::Release(); return 1; } // Implement IEnumPins. - STDMETHOD(Next)(ULONG count, IPin** pins, ULONG* fetched) { + STDMETHOD(Next)(ULONG count, IPin** pins, ULONG* fetched) override { ULONG pins_fetched = 0; while (pins_fetched < count && filter_->NoOfPins() > index_) { IPin* pin = filter_->GetPin(index_++); @@ -56,7 +56,7 @@ return pins_fetched == count ? S_OK : S_FALSE; } - STDMETHOD(Skip)(ULONG count) { + STDMETHOD(Skip)(ULONG count) override { if (filter_->NoOfPins()- index_ > count) { index_ += count; return S_OK; @@ -65,12 +65,12 @@ return S_FALSE; } - STDMETHOD(Reset)() { + STDMETHOD(Reset)() override { index_ = 0; return S_OK; } - STDMETHOD(Clone)(IEnumPins** clone) { + STDMETHOD(Clone)(IEnumPins** clone) override { PinEnumerator* pin_enum = new PinEnumerator(filter_.get()); pin_enum->AddRef(); pin_enum->index_ = index_;
diff --git a/media/video/capture/win/filter_base_win.h b/media/video/capture/win/filter_base_win.h index 9d5aa76f..e490f421 100644 --- a/media/video/capture/win/filter_base_win.h +++ b/media/video/capture/win/filter_base_win.h
@@ -30,36 +30,36 @@ virtual IPin* GetPin(int index) = 0; // Inherited from IUnknown. - STDMETHOD(QueryInterface)(REFIID id, void** object_ptr); - STDMETHOD_(ULONG, AddRef)(); - STDMETHOD_(ULONG, Release)(); + STDMETHOD(QueryInterface)(REFIID id, void** object_ptr) override; + STDMETHOD_(ULONG, AddRef)() override; + STDMETHOD_(ULONG, Release)() override; // Inherited from IBaseFilter. - STDMETHOD(EnumPins)(IEnumPins** enum_pins); + STDMETHOD(EnumPins)(IEnumPins** enum_pins) override; - STDMETHOD(FindPin)(LPCWSTR id, IPin** pin); + STDMETHOD(FindPin)(LPCWSTR id, IPin** pin) override; - STDMETHOD(QueryFilterInfo)(FILTER_INFO* info); + STDMETHOD(QueryFilterInfo)(FILTER_INFO* info) override; - STDMETHOD(JoinFilterGraph)(IFilterGraph* graph, LPCWSTR name); + STDMETHOD(JoinFilterGraph)(IFilterGraph* graph, LPCWSTR name) override; - STDMETHOD(QueryVendorInfo)(LPWSTR* vendor_info); + STDMETHOD(QueryVendorInfo)(LPWSTR* vendor_info) override; // Inherited from IMediaFilter. - STDMETHOD(Stop)(); + STDMETHOD(Stop)() override; - STDMETHOD(Pause)(); + STDMETHOD(Pause)() override; - STDMETHOD(Run)(REFERENCE_TIME start); + STDMETHOD(Run)(REFERENCE_TIME start) override; - STDMETHOD(GetState)(DWORD msec_timeout, FILTER_STATE* state); + STDMETHOD(GetState)(DWORD msec_timeout, FILTER_STATE* state) override; - STDMETHOD(SetSyncSource)(IReferenceClock* clock); + STDMETHOD(SetSyncSource)(IReferenceClock* clock) override; - STDMETHOD(GetSyncSource)(IReferenceClock** clock); + STDMETHOD(GetSyncSource)(IReferenceClock** clock) override; // Inherited from IPersistent. - STDMETHOD(GetClassID)(CLSID* class_id) = 0; + STDMETHOD(GetClassID)(CLSID* class_id) override = 0; private: FILTER_STATE state_;
diff --git a/media/video/capture/win/pin_base_win.cc b/media/video/capture/win/pin_base_win.cc index 92453c18..d2efa8b 100644 --- a/media/video/capture/win/pin_base_win.cc +++ b/media/video/capture/win/pin_base_win.cc
@@ -22,7 +22,7 @@ } // Implement from IUnknown. - STDMETHOD(QueryInterface)(REFIID iid, void** object_ptr) { + STDMETHOD(QueryInterface)(REFIID iid, void** object_ptr) override { if (iid == IID_IEnumMediaTypes || iid == IID_IUnknown) { AddRef(); *object_ptr = static_cast<IEnumMediaTypes*>(this); @@ -31,18 +31,18 @@ return E_NOINTERFACE; } - STDMETHOD_(ULONG, AddRef)() { + STDMETHOD_(ULONG, AddRef)() override { base::RefCounted<TypeEnumerator>::AddRef(); return 1; } - STDMETHOD_(ULONG, Release)() { + STDMETHOD_(ULONG, Release)() override { base::RefCounted<TypeEnumerator>::Release(); return 1; } // Implement IEnumMediaTypes. - STDMETHOD(Next)(ULONG count, AM_MEDIA_TYPE** types, ULONG* fetched) { + STDMETHOD(Next)(ULONG count, AM_MEDIA_TYPE** types, ULONG* fetched) override { ULONG types_fetched = 0; while (types_fetched < count) { @@ -81,17 +81,17 @@ return types_fetched == count ? S_OK : S_FALSE; } - STDMETHOD(Skip)(ULONG count) { + STDMETHOD(Skip)(ULONG count) override { index_ += count; return S_OK; } - STDMETHOD(Reset)() { + STDMETHOD(Reset)() override { index_ = 0; return S_OK; } - STDMETHOD(Clone)(IEnumMediaTypes** clone) { + STDMETHOD(Clone)(IEnumMediaTypes** clone) override { TypeEnumerator* type_enum = new TypeEnumerator(pin_.get()); type_enum->AddRef(); type_enum->index_ = index_;
diff --git a/media/video/capture/win/pin_base_win.h b/media/video/capture/win/pin_base_win.h index 33f403f..7ad5436 100644 --- a/media/video/capture/win/pin_base_win.h +++ b/media/video/capture/win/pin_base_win.h
@@ -40,59 +40,61 @@ // Called when new media is received. Note that this is not on the same // thread as where the pin is created. - STDMETHOD(Receive)(IMediaSample* sample) = 0; + STDMETHOD(Receive)(IMediaSample* sample) override = 0; - STDMETHOD(Connect)(IPin* receive_pin, const AM_MEDIA_TYPE* media_type); + STDMETHOD(Connect)(IPin* receive_pin, + const AM_MEDIA_TYPE* media_type) override; STDMETHOD(ReceiveConnection)(IPin* connector, - const AM_MEDIA_TYPE* media_type); + const AM_MEDIA_TYPE* media_type) override; - STDMETHOD(Disconnect)(); + STDMETHOD(Disconnect)() override; - STDMETHOD(ConnectedTo)(IPin** pin); + STDMETHOD(ConnectedTo)(IPin** pin) override; - STDMETHOD(ConnectionMediaType)(AM_MEDIA_TYPE* media_type); + STDMETHOD(ConnectionMediaType)(AM_MEDIA_TYPE* media_type) override; - STDMETHOD(QueryPinInfo)(PIN_INFO* info); + STDMETHOD(QueryPinInfo)(PIN_INFO* info) override; - STDMETHOD(QueryDirection)(PIN_DIRECTION* pin_dir); + STDMETHOD(QueryDirection)(PIN_DIRECTION* pin_dir) override; - STDMETHOD(QueryId)(LPWSTR* id); + STDMETHOD(QueryId)(LPWSTR* id) override; - STDMETHOD(QueryAccept)(const AM_MEDIA_TYPE* media_type); + STDMETHOD(QueryAccept)(const AM_MEDIA_TYPE* media_type) override; - STDMETHOD(EnumMediaTypes)(IEnumMediaTypes** types); + STDMETHOD(EnumMediaTypes)(IEnumMediaTypes** types) override; - STDMETHOD(QueryInternalConnections)(IPin** pins, ULONG* no_pins); + STDMETHOD(QueryInternalConnections)(IPin** pins, ULONG* no_pins) override; - STDMETHOD(EndOfStream)(); + STDMETHOD(EndOfStream)() override; - STDMETHOD(BeginFlush)(); + STDMETHOD(BeginFlush)() override; - STDMETHOD(EndFlush)(); + STDMETHOD(EndFlush)() override; STDMETHOD(NewSegment)(REFERENCE_TIME start, REFERENCE_TIME stop, - double dRate); + double dRate) override; // Inherited from IMemInputPin. - STDMETHOD(GetAllocator)(IMemAllocator** allocator); + STDMETHOD(GetAllocator)(IMemAllocator** allocator) override; - STDMETHOD(NotifyAllocator)(IMemAllocator* allocator, BOOL read_only); + STDMETHOD(NotifyAllocator)(IMemAllocator* allocator, BOOL read_only) override; - STDMETHOD(GetAllocatorRequirements)(ALLOCATOR_PROPERTIES* properties); + STDMETHOD(GetAllocatorRequirements)( + ALLOCATOR_PROPERTIES* properties) override; STDMETHOD(ReceiveMultiple)(IMediaSample** samples, long sample_count, - long* processed); - STDMETHOD(ReceiveCanBlock)(); + long* processed) override; + STDMETHOD(ReceiveCanBlock)() override; // Inherited from IUnknown. - STDMETHOD(QueryInterface)(REFIID id, void** object_ptr); + STDMETHOD(QueryInterface)(REFIID id, void** object_ptr) override; - STDMETHOD_(ULONG, AddRef)(); + STDMETHOD_(ULONG, AddRef)() override; - STDMETHOD_(ULONG, Release)(); + STDMETHOD_(ULONG, Release)() override; private: AM_MEDIA_TYPE current_media_type_;
diff --git a/media/video/capture/win/sink_filter_win.h b/media/video/capture/win/sink_filter_win.h index 831fd48..f9ff0c9 100644 --- a/media/video/capture/win/sink_filter_win.h +++ b/media/video/capture/win/sink_filter_win.h
@@ -35,7 +35,7 @@ SinkFilter : public FilterBase { public: explicit SinkFilter(SinkFilterObserver* observer); - virtual ~SinkFilter(); + ~SinkFilter() override; void SetRequestedMediaFormat(VideoPixelFormat pixel_format, float frame_rate, @@ -45,10 +45,10 @@ const VideoCaptureFormat& ResultingFormat(); // Implement FilterBase. - virtual size_t NoOfPins(); - virtual IPin* GetPin(int index); + size_t NoOfPins() override; + IPin* GetPin(int index) override; - STDMETHOD(GetClassID)(CLSID* clsid); + STDMETHOD(GetClassID)(CLSID* clsid) override; private: scoped_refptr<SinkInputPin> input_pin_;
diff --git a/media/video/capture/win/sink_input_pin_win.h b/media/video/capture/win/sink_input_pin_win.h index ffe5245..1ee23a9 100644 --- a/media/video/capture/win/sink_input_pin_win.h +++ b/media/video/capture/win/sink_input_pin_win.h
@@ -22,7 +22,7 @@ class SinkInputPin : public PinBase { public: SinkInputPin(IBaseFilter* filter, SinkFilterObserver* observer); - virtual ~SinkInputPin(); + ~SinkInputPin() override; void SetRequestedMediaFormat(VideoPixelFormat pixel_format, float frame_rate, @@ -32,10 +32,10 @@ const VideoCaptureFormat& ResultingFormat(); // Implement PinBase. - virtual bool IsMediaTypeValid(const AM_MEDIA_TYPE* media_type); - virtual bool GetValidMediaType(int index, AM_MEDIA_TYPE* media_type); + bool IsMediaTypeValid(const AM_MEDIA_TYPE* media_type) override; + bool GetValidMediaType(int index, AM_MEDIA_TYPE* media_type) override; - STDMETHOD(Receive)(IMediaSample* media_sample); + STDMETHOD(Receive)(IMediaSample* media_sample) override; private: VideoPixelFormat requested_pixel_format_;
diff --git a/media/video/capture/win/video_capture_device_factory_win.h b/media/video/capture/win/video_capture_device_factory_win.h index 849e1ad4..f76fe63 100644 --- a/media/video/capture/win/video_capture_device_factory_win.h +++ b/media/video/capture/win/video_capture_device_factory_win.h
@@ -19,12 +19,12 @@ static bool PlatformSupportsMediaFoundation(); VideoCaptureDeviceFactoryWin(); - virtual ~VideoCaptureDeviceFactoryWin() {} + ~VideoCaptureDeviceFactoryWin() override {} - virtual scoped_ptr<VideoCaptureDevice> Create( + scoped_ptr<VideoCaptureDevice> Create( const VideoCaptureDevice::Name& device_name) override; - virtual void GetDeviceNames(VideoCaptureDevice::Names* device_names) override; - virtual void GetDeviceSupportedFormats( + void GetDeviceNames(VideoCaptureDevice::Names* device_names) override; + void GetDeviceSupportedFormats( const VideoCaptureDevice::Name& device, VideoCaptureFormats* supported_formats) override;
diff --git a/media/video/capture/win/video_capture_device_mf_win.cc b/media/video/capture/win/video_capture_device_mf_win.cc index 3d3fb160..95ade71 100644 --- a/media/video/capture/win/video_capture_device_mf_win.cc +++ b/media/video/capture/win/video_capture_device_mf_win.cc
@@ -91,7 +91,7 @@ wait_event_ = event; } - STDMETHOD(QueryInterface)(REFIID riid, void** object) { + STDMETHOD(QueryInterface)(REFIID riid, void** object) override { if (riid != IID_IUnknown && riid != IID_IMFSourceReaderCallback) return E_NOINTERFACE; *object = static_cast<IMFSourceReaderCallback*>(this); @@ -99,18 +99,21 @@ return S_OK; } - STDMETHOD_(ULONG, AddRef)() { + STDMETHOD_(ULONG, AddRef)() override { base::RefCountedThreadSafe<MFReaderCallback>::AddRef(); return 1U; } - STDMETHOD_(ULONG, Release)() { + STDMETHOD_(ULONG, Release)() override { base::RefCountedThreadSafe<MFReaderCallback>::Release(); return 1U; } - STDMETHOD(OnReadSample)(HRESULT status, DWORD stream_index, - DWORD stream_flags, LONGLONG time_stamp, IMFSample* sample) { + STDMETHOD(OnReadSample)(HRESULT status, + DWORD stream_index, + DWORD stream_flags, + LONGLONG time_stamp, + IMFSample* sample) override { base::TimeTicks stamp(base::TimeTicks::Now()); if (!sample) { observer_->OnIncomingCapturedData(NULL, 0, 0, stamp); @@ -134,7 +137,7 @@ return S_OK; } - STDMETHOD(OnFlush)(DWORD stream_index) { + STDMETHOD(OnFlush)(DWORD stream_index) override { if (wait_event_) { wait_event_->Signal(); wait_event_ = NULL; @@ -142,7 +145,7 @@ return S_OK; } - STDMETHOD(OnEvent)(DWORD stream_index, IMFMediaEvent* event) { + STDMETHOD(OnEvent)(DWORD stream_index, IMFMediaEvent* event) override { NOTIMPLEMENTED(); return S_OK; }
diff --git a/media/video/capture/win/video_capture_device_mf_win.h b/media/video/capture/win/video_capture_device_mf_win.h index fc11d19..eeb7edf 100644 --- a/media/video/capture/win/video_capture_device_mf_win.h +++ b/media/video/capture/win/video_capture_device_mf_win.h
@@ -36,16 +36,15 @@ static bool FormatFromGuid(const GUID& guid, VideoPixelFormat* format); explicit VideoCaptureDeviceMFWin(const Name& device_name); - virtual ~VideoCaptureDeviceMFWin(); + ~VideoCaptureDeviceMFWin() override; // Opens the device driver for this device. bool Init(const base::win::ScopedComPtr<IMFMediaSource>& source); // VideoCaptureDevice implementation. - virtual void AllocateAndStart(const VideoCaptureParams& params, - scoped_ptr<VideoCaptureDevice::Client> client) - override; - virtual void StopAndDeAllocate() override; + void AllocateAndStart(const VideoCaptureParams& params, + scoped_ptr<VideoCaptureDevice::Client> client) override; + void StopAndDeAllocate() override; // Captured new video data. void OnIncomingCapturedData(const uint8* data,
diff --git a/media/video/capture/win/video_capture_device_win.h b/media/video/capture/win/video_capture_device_win.h index f758c2c..9315754f 100644 --- a/media/video/capture/win/video_capture_device_win.h +++ b/media/video/capture/win/video_capture_device_win.h
@@ -64,15 +64,14 @@ const GUID& sub_type); explicit VideoCaptureDeviceWin(const Name& device_name); - virtual ~VideoCaptureDeviceWin(); + ~VideoCaptureDeviceWin() override; // Opens the device driver for this device. bool Init(); // VideoCaptureDevice implementation. - virtual void AllocateAndStart( - const VideoCaptureParams& params, - scoped_ptr<VideoCaptureDevice::Client> client) override; - virtual void StopAndDeAllocate() override; + void AllocateAndStart(const VideoCaptureParams& params, + scoped_ptr<VideoCaptureDevice::Client> client) override; + void StopAndDeAllocate() override; private: enum InternalState { @@ -83,7 +82,7 @@ }; // Implements SinkFilterObserver. - virtual void FrameReceived(const uint8* buffer, int length); + void FrameReceived(const uint8* buffer, int length) override; bool CreateCapabilityMap(); void SetAntiFlickerInCaptureFilter();
diff --git a/mojo/application/application_test_main_chromium.cc b/mojo/application/application_test_main_chromium.cc index 3d6c4b2..6b21abe 100644 --- a/mojo/application/application_test_main_chromium.cc +++ b/mojo/application/application_test_main_chromium.cc
@@ -13,8 +13,7 @@ // An AtExitManager instance is needed to construct message loops. base::AtExitManager at_exit; - // Initialize test timeouts, which requires CommandLine::ForCurrentProcess(). - // TODO(msw): Plumb relevant command line args before initializing timeouts. + // Initialize the current process Commandline and test timeouts. mojo::ApplicationRunnerChromium::InitBaseCommandLine(); TestTimeouts::Initialize();
diff --git a/mojo/converters/surfaces/surfaces_type_converters.cc b/mojo/converters/surfaces/surfaces_type_converters.cc index 3874e6a7..f922ebf9 100644 --- a/mojo/converters/surfaces/surfaces_type_converters.cc +++ b/mojo/converters/surfaces/surfaces_type_converters.cc
@@ -363,7 +363,7 @@ PassPtr TypeConverter<PassPtr, cc::RenderPass>::Convert( const cc::RenderPass& input) { PassPtr pass = Pass::New(); - pass->id = input.id.index; + pass->id = RenderPassId::From(input.id); pass->output_rect = Rect::From(input.output_rect); pass->damage_rect = Rect::From(input.damage_rect); pass->transform_to_root_target = @@ -402,7 +402,7 @@ const PassPtr& input) { scoped_ptr<cc::RenderPass> pass = cc::RenderPass::Create( input->shared_quad_states.size(), input->quads.size()); - pass->SetAll(cc::RenderPassId(1, input->id), + pass->SetAll(input->id.To<cc::RenderPassId>(), input->output_rect.To<gfx::Rect>(), input->damage_rect.To<gfx::Rect>(), input->transform_to_root_target.To<gfx::Transform>(),
diff --git a/mojo/converters/surfaces/surfaces_utils.cc b/mojo/converters/surfaces/surfaces_utils.cc index 7853863a..b433722a 100644 --- a/mojo/converters/surfaces/surfaces_utils.cc +++ b/mojo/converters/surfaces/surfaces_utils.cc
@@ -27,7 +27,10 @@ PassPtr CreateDefaultPass(int id, const gfx::Rect& rect) { PassPtr pass = Pass::New(); - pass->id = id; + RenderPassId render_pass_id; + render_pass_id.layer_id = 1; + render_pass_id.index = id; + pass->id = render_pass_id.Clone(); pass->output_rect = Rect::From(rect); pass->damage_rect = Rect::From(rect); pass->transform_to_root_target = Transform::From(gfx::Transform());
diff --git a/mojo/converters/surfaces/tests/surface_unittest.cc b/mojo/converters/surfaces/tests/surface_unittest.cc index 10e1b41..465bee3 100644 --- a/mojo/converters/surfaces/tests/surface_unittest.cc +++ b/mojo/converters/surfaces/tests/surface_unittest.cc
@@ -287,7 +287,7 @@ PassPtr mojo_pass = Pass::From(*pass); ASSERT_FALSE(mojo_pass.is_null()); - EXPECT_EQ(6, mojo_pass->id); + EXPECT_EQ(6, mojo_pass->id->index); EXPECT_EQ(Rect::From(output_rect), mojo_pass->output_rect); EXPECT_EQ(Rect::From(damage_rect), mojo_pass->damage_rect); EXPECT_EQ(Transform::From(transform_to_root_target),
diff --git a/mojo/mojo_nacl_untrusted.gyp b/mojo/mojo_nacl_untrusted.gyp index eeca34221..4054b32 100644 --- a/mojo/mojo_nacl_untrusted.gyp +++ b/mojo/mojo_nacl_untrusted.gyp
@@ -24,7 +24,6 @@ '<(monacl_codegen_dir)/libmojo.cc', ], 'dependencies': [ - '<(DEPTH)/native_client/tools.gyp:prep_toolchain', 'mojo_nacl.gyp:monacl_codegen', '../third_party/mojo/mojo_public.gyp:mojo_system_placeholder', ], @@ -44,7 +43,6 @@ '<(monacl_codegen_dir)/mojo_irt.h', ], 'dependencies': [ - '<(DEPTH)/native_client/tools.gyp:prep_toolchain', 'mojo_nacl.gyp:monacl_codegen', ], 'direct_dependent_settings': { @@ -71,7 +69,6 @@ ], }, 'dependencies': [ - '<(DEPTH)/native_client/tools.gyp:prep_toolchain', '<(DEPTH)/native_client/src/untrusted/nacl/nacl.gyp:nacl_lib', '<(DEPTH)/native_client/src/untrusted/nacl/nacl.gyp:imc_syscalls_lib', '<(DEPTH)/native_client/src/untrusted/pthread/pthread.gyp:pthread_lib',
diff --git a/mojo/services/BUILD.gn b/mojo/services/BUILD.gn index 304ef92..455e190 100644 --- a/mojo/services/BUILD.gn +++ b/mojo/services/BUILD.gn
@@ -20,12 +20,12 @@ if (!is_component_build) { deps += [ "//components/clipboard", + "//components/html_viewer", "//components/kiosk_wm:window_manager", "//components/native_viewport", "//components/surfaces", "//components/view_manager", "//components/window_manager:lib", - "//mojo/services/html_viewer", "//mojo/services/network", "//mojo/services/tracing", ] @@ -33,11 +33,11 @@ # TODO(GYP): Make this work. if (is_mac) { deps -= [ + "//components/html_viewer", "//components/kiosk_wm:window_manager", "//components/native_viewport", "//components/view_manager", "//components/window_manager:lib", - "//mojo/services/html_viewer", ] } } @@ -50,17 +50,17 @@ ] if (!is_component_build) { deps += [ + "//components/html_viewer:tests", "//components/view_manager:view_manager_service_unittests", "//components/window_manager:window_manager_unittests", - "//mojo/services/html_viewer:tests", ] # TODO(GYP): Make this work. if (is_mac) { deps -= [ + "//components/html_viewer:tests", "//components/view_manager:view_manager_service_unittests", "//components/window_manager:window_manager_unittests", - "//mojo/services/html_viewer:tests", ] } } @@ -74,18 +74,18 @@ if (!is_component_build) { deps += [ "//components/clipboard:apptests", + "//components/html_viewer:apptests", "//components/view_manager:apptests", "//components/window_manager:apptests", - "//mojo/services/html_viewer:apptests", "//mojo/services/network:apptests", ] # TODO(GYP): Make this work. if (is_mac) { deps -= [ + "//components/html_viewer:apptests", "//components/view_manager:apptests", "//components/window_manager:apptests", - "//mojo/services/html_viewer:apptests", ] } }
diff --git a/mojo/services/network/public/interfaces/url_loader.mojom b/mojo/services/network/public/interfaces/url_loader.mojom index 10f0af3..77db3ad 100644 --- a/mojo/services/network/public/interfaces/url_loader.mojom +++ b/mojo/services/network/public/interfaces/url_loader.mojom
@@ -34,6 +34,9 @@ // servers to also not satisfy the request from their cache. This has the // effect of forcing a full end-to-end fetch. bool bypass_cache = false; + + // The referrer request header. + string? referrer; }; struct URLResponse { @@ -67,6 +70,7 @@ // follow this redirect. string? redirect_method; string? redirect_url; + string? redirect_referrer; }; struct URLLoaderStatus {
diff --git a/mojo/services/network/url_loader_impl.cc b/mojo/services/network/url_loader_impl.cc index 964f9c2..467cc3fa 100644 --- a/mojo/services/network/url_loader_impl.cc +++ b/mojo/services/network/url_loader_impl.cc
@@ -129,6 +129,10 @@ url_request_ = context_->url_request_context()->CreateRequest( GURL(request->url), net::DEFAULT_PRIORITY, this); url_request_->set_method(request->method); + url_request_->SetReferrer(request->referrer); + // TODO(jam): need to specify this policy. + url_request_->set_referrer_policy( + net::URLRequest::CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE); if (request->headers) { net::HttpRequestHeaders headers; for (size_t i = 0; i < request->headers.size(); ++i) @@ -207,6 +211,7 @@ URLResponsePtr response = MakeURLResponse(url_request); response->redirect_method = redirect_info.new_method; response->redirect_url = String::From(redirect_info.new_url); + response->redirect_referrer = redirect_info.new_referrer; SendResponse(response.Pass());
diff --git a/mojo/shell/BUILD.gn b/mojo/shell/BUILD.gn index 512c455..3c06107 100644 --- a/mojo/shell/BUILD.gn +++ b/mojo/shell/BUILD.gn
@@ -18,24 +18,17 @@ deps = [ ":mojo_shell", ":tests", + "//mojo/shell/application_manager:tests", ] if (is_android) { deps += [ ":mojo_shell_apk", - ":mojo_shell_tests_apk", + ":tests_apk", ] } } -group("tests") { - testonly = true - deps = [ - ":mojo_shell_tests", - "//mojo/shell/application_manager:mojo_application_manager_unittests", - ] -} - if (is_android) { import("//build/config/android/config.gni") import("//build/config/android/rules.gni") @@ -45,7 +38,6 @@ sources = [] deps = [ - ":init", ":lib", "//base", "//build/config/sanitizers:deps", @@ -79,17 +71,6 @@ } } -source_set("init") { - sources = [ - "init.cc", - "init.h", - ] - - deps = [ - "//base", - ] -} - source_set("in_process_native_runner") { sources = [ "in_process_native_runner.cc", @@ -116,6 +97,8 @@ "context.h", "filename_util.cc", "filename_util.h", + "init.cc", + "init.h", "out_of_process_native_runner.cc", "out_of_process_native_runner.h", "task_runners.cc", @@ -126,7 +109,6 @@ deps = [ ":child_process_bindings", - ":init", ":in_process_native_runner", ":native_application_support", "//base", @@ -350,8 +332,9 @@ ] } -# GYP version: mojo/mojo.gyp:mojo_shell_tests -test("mojo_shell_tests") { +test("tests") { + output_name = "mojo_shell_unittests" + sources = [ "child_process_host_unittest.cc", "data_pipe_peek_unittest.cc", @@ -400,22 +383,6 @@ } } -# GYP version: mojo/mojo.gyp:mojo_shell_test_support -source_set("test_support") { - sources = [ - "shell_test_helper.cc", - "shell_test_helper.h", - ] - - deps = [ - ":init", - ":lib", - "//base", - "//third_party/mojo/src/mojo/edk/system", - "//mojo/shell/application_manager", - ] -} - mojo_native_application("apptests") { output_name = "shell_apptests"
diff --git a/mojo/shell/application_manager/BUILD.gn b/mojo/shell/application_manager/BUILD.gn index f220b43..9122e7e9 100644 --- a/mojo/shell/application_manager/BUILD.gn +++ b/mojo/shell/application_manager/BUILD.gn
@@ -47,7 +47,9 @@ ] } -test("mojo_application_manager_unittests") { +test("tests") { + output_name = "mojo_application_manager_unittests" + sources = [ "application_manager_unittest.cc", "query_util_unittest.cc",
diff --git a/mojo/shell/application_manager/application_manager_unittest.cc b/mojo/shell/application_manager/application_manager_unittest.cc index cc420a7..f801e36 100644 --- a/mojo/shell/application_manager/application_manager_unittest.cc +++ b/mojo/shell/application_manager/application_manager_unittest.cc
@@ -487,6 +487,33 @@ EXPECT_EQ(0U, app_args.size()); } +// Confirm that url mappings are respected. +TEST_F(ApplicationManagerTest, URLMapping) { + ApplicationManager am(&test_delegate_); + GURL test_url("test:test"); + GURL test_url2("test:test2"); + test_delegate_.AddMapping(test_url, test_url2); + TestApplicationLoader* loader = new TestApplicationLoader; + loader->set_context(&context_); + am.SetLoaderForURL(scoped_ptr<ApplicationLoader>(loader), test_url2); + { + // Connext to the mapped url + TestServicePtr test_service; + am.ConnectToService(test_url, &test_service); + TestClient test_client(test_service.Pass()); + test_client.Test("test"); + loop_.Run(); + } + { + // Connext to the target url + TestServicePtr test_service; + am.ConnectToService(test_url2, &test_service); + TestClient test_client(test_service.Pass()); + test_client.Test("test"); + loop_.Run(); + } +} + TEST_F(ApplicationManagerTest, ClientError) { test_client_->Test("test"); EXPECT_TRUE(HasFactoryForTestURL());
diff --git a/mojo/shell/context.cc b/mojo/shell/context.cc index fc56476f..0cc27d1 100644 --- a/mojo/shell/context.cc +++ b/mojo/shell/context.cc
@@ -84,6 +84,23 @@ resolver->AddOriginMapping(GURL(origin_mapping.origin), GURL(origin_mapping.base_url)); + if (command_line.HasSwitch(switches::kURLMappings)) { + const std::string mappings = + command_line.GetSwitchValueASCII(switches::kURLMappings); + + base::StringPairs pairs; + if (!base::SplitStringIntoKeyValuePairs(mappings, '=', ',', &pairs)) + return false; + using StringPair = std::pair<std::string, std::string>; + for (const StringPair& pair : pairs) { + const GURL from(pair.first); + const GURL to = context->ResolveCommandLineURL(pair.second); + if (!from.is_valid() || !to.is_valid()) + return false; + resolver->AddURLMapping(from, to); + } + } + return true; }
diff --git a/mojo/shell/shell_test_helper.cc b/mojo/shell/shell_test_helper.cc deleted file mode 100644 index 0a467303..0000000 --- a/mojo/shell/shell_test_helper.cc +++ /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. - -#include "mojo/shell/shell_test_helper.h" - -#include "base/command_line.h" -#include "base/files/file_path.h" -#include "base/files/file_util.h" -#include "base/logging.h" -#include "base/path_service.h" -#include "mojo/shell/filename_util.h" -#include "mojo/shell/init.h" -#include "mojo/shell/url_resolver.h" - -namespace mojo { -namespace shell { - -ShellTestHelper::ShellTestHelper() { - base::CommandLine::Init(0, nullptr); - InitializeLogging(); -} - -ShellTestHelper::~ShellTestHelper() { -} - -void ShellTestHelper::Init() { - context_.Init(); - test_api_.reset( - new ApplicationManager::TestAPI(context_.application_manager())); - base::FilePath service_dir; - CHECK(PathService::Get(base::DIR_MODULE, &service_dir)); - context_.url_resolver()->SetMojoBaseURL(FilePathToFileURL(service_dir)); -} - -void ShellTestHelper::SetLoaderForURL(scoped_ptr<ApplicationLoader> loader, - const GURL& url) { - context_.application_manager()->SetLoaderForURL(loader.Pass(), url); -} - -void ShellTestHelper::AddURLMapping(const GURL& url, const GURL& resolved_url) { - context_.url_resolver()->AddURLMapping(url, resolved_url); -} - -} // namespace shell -} // namespace mojo
diff --git a/mojo/shell/shell_test_helper.h b/mojo/shell/shell_test_helper.h deleted file mode 100644 index 185d367..0000000 --- a/mojo/shell/shell_test_helper.h +++ /dev/null
@@ -1,52 +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 SHELL_SHELL_TEST_HELPER_H_ -#define SHELL_SHELL_TEST_HELPER_H_ - -#include "base/macros.h" -#include "base/memory/scoped_ptr.h" -#include "base/run_loop.h" -#include "mojo/shell/application_manager/application_loader.h" -#include "mojo/shell/context.h" - -class GURL; - -namespace mojo { -namespace shell { - -// ShellTestHelper is useful for tests to establish a connection to the -// ApplicationManager. Invoke Init() to establish the connection. Once done, -// application_manager() returns the ApplicationManager. -class ShellTestHelper { - public: - ShellTestHelper(); - ~ShellTestHelper(); - - void Init(); - - ApplicationManager* application_manager() { - return context_.application_manager(); - } - - // Sets a ApplicationLoader for the specified URL. |loader| is ultimately used - // on - // the thread this class spawns. - void SetLoaderForURL(scoped_ptr<ApplicationLoader> loader, const GURL& url); - - // Adds a mapping that is used when resolving mojo urls. See URLResolver - // for details. - void AddURLMapping(const GURL& url, const GURL& resolved_url); - - private: - Context context_; - base::MessageLoop shell_loop_; - scoped_ptr<ApplicationManager::TestAPI> test_api_; - DISALLOW_COPY_AND_ASSIGN(ShellTestHelper); -}; - -} // namespace shell -} // namespace mojo - -#endif // SHELL_SHELL_TEST_HELPER_H_
diff --git a/mojo/shell/switches.cc b/mojo/shell/switches.cc index 28e7d058..1fff40d5 100644 --- a/mojo/shell/switches.cc +++ b/mojo/shell/switches.cc
@@ -55,4 +55,10 @@ // seconds or when the shell exits. const char kTraceStartup[] = "trace-startup"; +// Specifies a set of mappings to apply when resolving urls. The value is a set +// of ',' separated mappings, where each mapping consists of a pair of urls +// giving the to/from url 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"; + } // namespace switches
diff --git a/mojo/shell/switches.h b/mojo/shell/switches.h index ff2eb6c..31cac8f6 100644 --- a/mojo/shell/switches.h +++ b/mojo/shell/switches.h
@@ -23,6 +23,7 @@ extern const char kOrigin[]; extern const char kPredictableAppFilenames[]; extern const char kTraceStartup[]; +extern const char kURLMappings[]; } // namespace switches
diff --git a/mojo/tools/apptest_runner.py b/mojo/tools/apptest_runner.py index 9ef7b7aa..ddc0830 100755 --- a/mojo/tools/apptest_runner.py +++ b/mojo/tools/apptest_runner.py
@@ -1,81 +1,91 @@ #!/usr/bin/env python -# Copyright 2015 The Chromium Authors. All rights reserved. +# 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. """A test runner for gtest application tests.""" import argparse -import ast import logging -import os import sys -_logging = logging.getLogger() +from mopy import dart_apptest +from mopy import gtest +# TODO(msw): Mojo's script pulls in android.py via mojo/devtools/common/pylib. +from mopy.android import AndroidShell +from mopy.config import Config +from mopy.gn import ConfigForGNArgs, ParseGNConfig +from mopy.log import InitLogging +from mopy.paths import Paths -import gtest + +_logger = logging.getLogger() def main(): - logging.basicConfig() - # Uncomment to debug: - #_logging.setLevel(logging.DEBUG) + parser = argparse.ArgumentParser(description="A test runner for application " + "tests.") - parser = argparse.ArgumentParser(description='A test runner for gtest ' - 'application tests.') - - parser.add_argument('apptest_list_file', type=file, - help='A file listing apptests to run.') - parser.add_argument('build_dir', type=str, - help='The build output directory.') + parser.add_argument("--verbose", help="be verbose (multiple times for more)", + default=0, dest="verbose_count", action="count") + parser.add_argument("test_list_file", type=file, + help="a file listing apptests to run") + parser.add_argument("build_dir", type=str, + help="the build output directory") args = parser.parse_args() - apptest_list = ast.literal_eval(args.apptest_list_file.read()) - _logging.debug("Test list: %s" % apptest_list) + InitLogging(args.verbose_count) + config = ConfigForGNArgs(ParseGNConfig(args.build_dir)) + + _logger.debug("Test list file: %s", args.test_list_file) + execution_globals = {"config": config} + exec args.test_list_file in execution_globals + test_list = execution_globals["tests"] + _logger.debug("Test list: %s" % test_list) + + extra_args = [] + if config.target_os == Config.OS_ANDROID: + paths = Paths(config) + shell = AndroidShell(paths.target_mojo_shell_path, paths.build_dir, + paths.adb_path) + extra_args.extend(shell.PrepareShellRun(fixed_port=False)) + else: + shell = None gtest.set_color() - mojo_shell_path = os.path.join(args.build_dir, "mojo_shell") exit_code = 0 - for apptest_dict in apptest_list: - if apptest_dict.get("disabled"): - continue + for test_dict in test_list: + test = test_dict["test"] + test_name = test_dict.get("name", test) + test_type = test_dict.get("type", "gtest") + test_args = test_dict.get("test-args", []) + shell_args = test_dict.get("shell-args", []) + extra_args - apptest = apptest_dict["test"] - apptest_args = apptest_dict.get("test-args", []) - shell_args = apptest_dict.get("shell-args", []) - - print "Running " + apptest + "...", + _logger.info("Will start: %s" % test_name) + print "Running %s...." % test_name, sys.stdout.flush() - # List the apptest fixtures so they can be run independently for isolation. - # TODO(msw): Run some apptests without fixture isolation? - fixtures = gtest.get_fixtures(mojo_shell_path, apptest) + if test_type == "dart": + apptest_result = dart_apptest.run_test(config, shell, test_dict, + shell_args, {test: test_args}) + elif test_type == "gtest": + apptest_result = gtest.run_fixtures(config, shell, test_dict, + test, False, + test_args, shell_args) + elif test_type == "gtest_isolated": + apptest_result = gtest.run_fixtures(config, shell, test_dict, + test, True, test_args, shell_args) + else: + apptest_result = "Invalid test type in %r" % test_dict - if not fixtures: - print "Failed with no tests found." + if apptest_result != "Succeeded": exit_code = 1 - continue - - apptest_result = "Succeeded" - for fixture in fixtures: - args_for_apptest = " ".join(["--gtest_filter=" + fixture] + apptest_args) - - success = RunApptestInShell(mojo_shell_path, apptest, - shell_args + [args_for_apptest]) - - if not success: - apptest_result = "Failed test(s) in %r" % apptest_dict - exit_code = 1 - print apptest_result + _logger.info("Completed: %s" % test_name) return exit_code -def RunApptestInShell(mojo_shell_path, apptest, shell_args): - return gtest.run_test([mojo_shell_path, apptest] + shell_args) - - if __name__ == '__main__': sys.exit(main())
diff --git a/mojo/tools/data/apptests b/mojo/tools/data/apptests index 400f70c..7f336c6 100644 --- a/mojo/tools/data/apptests +++ b/mojo/tools/data/apptests
@@ -1,10 +1,51 @@ # This file contains a list of Mojo gtest unit tests. -# This must be a valid python dictionary. -[ +# +# This must be valid Python. It may use the |config| global that will be a +# mopy.config.Config object, and must set a |tests| global that will contain the +# list of tests to run. +# +# The entries in |tests| are dictionaries of the following form: +# { +# # Required URL for apptest. +# "test": "mojo:test_app_url", +# # Optional display name (otherwise the entry for "test" above is used). +# "name": "mojo:test_app_url (more details)", +# # Optional test type. Valid values: +# # * "gtest" (default) +# # * "gtest_isolated": like "gtest", but run with fixture isolation, +# # i.e., each test in a fresh mojo_shell) +# # * "dart". +# "type": "gtest", +# # Optional arguments for the apptest. +# "test-args": ["--an_arg", "another_arg"], +# # Optional arguments for the shell. +# "shell-args": ["--some-flag-for-the-shell", "--another-flag"], +# } +# +# TODO(vtl|msw): Add a way of specifying data dependencies. + +tests = [ { - "test": "mojo:network_service_apptests", + "test": "mojo:clipboard_apptests", }, { "test": "mojo:html_viewer_apptests", + "shell-args": ["--is-headless"], }, + { + "test": "mojo:network_service_apptests", + }, + # TODO(msw|jam): Fix and enable the shell_apptests: http://crbug.com/479316 + #{ + # "test": "mojo:shell_apptests", + #}, + { + "test": "mojo:view_manager_apptests", + "type": "gtest_isolated", + "shell-args": ["--url-mappings=mojo:window_manager=mojo:test_window_manager"] + }, + # TODO(msw): Fix and enable window_manager_apptests: http://crbug.com/480040 + #{ + # "test": "mojo:window_manager_apptests", + #}, ]
diff --git a/mojo/tools/gtest.py b/mojo/tools/gtest.py deleted file mode 100644 index d84c8e5e..0000000 --- a/mojo/tools/gtest.py +++ /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. - -import logging -import os -import re -import subprocess -import sys - -_logging = logging.getLogger() - -def _print_process_error(command_line, error): - """Properly format an exception raised from a failed command execution.""" - - if command_line: - print 'Failed command: %r' % command_line - else: - print 'Failed command:' - print 72 * '-' - - if hasattr(error, 'returncode'): - print ' with exit code %d' % error.returncode - print 72 * '-' - - if hasattr(error, 'output'): - print error.output - else: - print error - print 72 * '-' - -def set_color(): - """Run gtests with color if we're on a TTY (and we're not being told - explicitly what to do).""" - if sys.stdout.isatty() and 'GTEST_COLOR' not in os.environ: - _logging.debug("Setting GTEST_COLOR=yes") - os.environ['GTEST_COLOR'] = 'yes' - - -def _try_command_line(command_line): - """Returns the output of a command line or an empty string on error.""" - _logging.debug("Running command line: %s" % command_line) - try: - return subprocess.check_output(command_line, stderr=subprocess.STDOUT) - except Exception as e: - _print_process_error(command_line, e) - return None - - -def run_test(command_line): - """Runs a command line and checks the output for signs of gtest failure.""" - output = _try_command_line(command_line) - # Fail on output with gtest's "[ FAILED ]" or a lack of "[ PASSED ]". - # The latter condition ensures failure on broken command lines or output. - # Check output instead of exit codes because mojo_shell always exits with 0. - if (output is None or - (output.find("[ FAILED ]") != -1 or output.find("[ PASSED ]") == -1)): - print "Failed test:" - _print_process_error(command_line, output) - return False - _logging.debug("Succeeded with output:\n%s" % output) - return True - - -def get_fixtures(mojo_shell, apptest): - """Returns the "Test.Fixture" list from an apptest using mojo_shell. - - Tests are listed by running the given apptest in mojo_shell and passing - --gtest_list_tests. The output is parsed and reformatted into a list like - [TestSuite.TestFixture, ... ] - An empty list is returned on failure, with errors logged. - """ - command = [mojo_shell, "--gtest_list_tests", apptest] - try: - list_output = subprocess.check_output(command, stderr=subprocess.STDOUT) - _logging.debug("Tests listed:\n%s" % list_output) - return _gtest_list_tests(list_output) - except Exception as e: - print "Failed to get test fixtures:" - _print_process_error(command, e) - return [] - - -def _gtest_list_tests(gtest_list_tests_output): - """Returns a list of strings formatted as TestSuite.TestFixture from the - output of running --gtest_list_tests on a GTEST application.""" - - # Remove log lines. - gtest_list_tests_output = ( - re.sub("^\[.*\n", "", gtest_list_tests_output, flags=re.MULTILINE)) - - if not re.match("^(\w*\.\r?\n( \w*\r?\n)+)+", gtest_list_tests_output): - raise Exception("Unrecognized --gtest_list_tests output:\n%s" % - gtest_list_tests_output) - - output_lines = gtest_list_tests_output.split('\n') - - test_list = [] - for line in output_lines: - if not line: - continue - if line[0] != ' ': - suite = line.strip() - continue - test_list.append(suite + line.strip()) - - return test_list
diff --git a/mojo/tools/mopy/android.py b/mojo/tools/mopy/android.py index 6584b9f..374ebb08 100644 --- a/mojo/tools/mopy/android.py +++ b/mojo/tools/mopy/android.py
@@ -3,8 +3,13 @@ # found in the LICENSE file. import atexit +import datetime +import email.utils +import hashlib +import itertools import json import logging +import math import os import os.path import random @@ -17,9 +22,6 @@ import SimpleHTTPServer import SocketServer -from mopy.config import Config -from mopy.paths import Paths - # Tags used by the mojo shell application logs. LOGCAT_TAGS = [ @@ -31,11 +33,30 @@ 'chromium', ] -ADB_PATH = os.path.join(Paths().src_root, 'third_party', 'android_tools', 'sdk', - 'platform-tools', 'adb') - MOJO_SHELL_PACKAGE_NAME = 'org.chromium.mojo.shell' +MAPPING_PREFIX = '--map-origin=' + +DEFAULT_BASE_PORT = 31337 + +ZERO = datetime.timedelta(0) + +class UTC_TZINFO(datetime.tzinfo): + """UTC time zone representation.""" + + def utcoffset(self, _): + return ZERO + + def tzname(self, _): + return "UTC" + + def dst(self, _): + return ZERO + +UTC = UTC_TZINFO() + +_logger = logging.getLogger() + class _SilentTCPServer(SocketServer.TCPServer): """ @@ -58,6 +79,69 @@ |base_path| directory over http. """ + def __init__(self, *args, **kwargs): + self.etag = None + SimpleHTTPServer.SimpleHTTPRequestHandler.__init__(self, *args, **kwargs) + + def get_etag(self): + if self.etag: + return self.etag + + path = self.translate_path(self.path) + if not os.path.isfile(path): + return None + + sha256 = hashlib.sha256() + BLOCKSIZE = 65536 + with open(path, 'rb') as hashed: + buf = hashed.read(BLOCKSIZE) + while len(buf) > 0: + sha256.update(buf) + buf = hashed.read(BLOCKSIZE) + self.etag = '"%s"' % sha256.hexdigest() + return self.etag + + def send_head(self): + # Always close the connection after each request, as the keep alive + # support from SimpleHTTPServer doesn't like when the client requests to + # close the connection before downloading the full response content. + # pylint: disable=W0201 + self.close_connection = 1 + + path = self.translate_path(self.path) + if os.path.isfile(path): + # Handle If-None-Match + etag = self.get_etag() + if ('If-None-Match' in self.headers and + etag == self.headers['If-None-Match']): + self.send_response(304) + return None + + # Handle If-Modified-Since + if ('If-None-Match' not in self.headers and + 'If-Modified-Since' in self.headers): + last_modified = datetime.datetime.fromtimestamp( + math.floor(os.stat(path).st_mtime), tz=UTC) + ims = datetime.datetime( + *email.utils.parsedate(self.headers['If-Modified-Since'])[:6], + tzinfo=UTC) + if last_modified <= ims: + self.send_response(304) + return None + + return SimpleHTTPServer.SimpleHTTPRequestHandler.send_head(self) + + def end_headers(self): + path = self.translate_path(self.path) + + if os.path.isfile(path): + etag = self.get_etag() + if etag: + self.send_header('ETag', etag) + self.send_header('Cache-Control', 'must-revalidate') + + return SimpleHTTPServer.SimpleHTTPRequestHandler.end_headers(self) + def translate_path(self, path): path_from_current = ( SimpleHTTPServer.SimpleHTTPRequestHandler.translate_path(self, path)) @@ -69,9 +153,26 @@ """ pass + RequestHandler.protocol_version = 'HTTP/1.1' return RequestHandler +def _IsMapOrigin(arg): + """Returns whether arg is a --map-origin argument.""" + return arg.startswith(MAPPING_PREFIX) + + +def _Split(l, pred): + positive = [] + negative = [] + for v in l: + if pred(v): + positive.append(v) + else: + negative.append(v) + return (positive, negative) + + def _ExitIfNeeded(process): """ Exits |process| if it is still alive. @@ -80,186 +181,228 @@ process.kill() -def _ReadFifo(fifo_path, pipe, on_fifo_closed, max_attempts=5): +class AndroidShell(object): + """ Allows to set up and run a given mojo shell binary on an Android device. + + Args: + shell_apk_path: path to the shell Android binary + local_dir: directory where locally build Mojo apps will be served, optional + adb_path: path to adb, optional if adb is in PATH + target_device: device to run on, if multiple devices are connected """ - Reads |fifo_path| on the device and write the contents to |pipe|. Calls - |on_fifo_closed| when the fifo is closed. This method will try to find the - path up to |max_attempts|, waiting 1 second between each attempt. If it cannot - find |fifo_path|, a exception will be raised. - """ - def Run(): - def _WaitForFifo(): - command = [ADB_PATH, 'shell', 'test -e "%s"; echo $?' % fifo_path] - for _ in xrange(max_attempts): - if subprocess.check_output(command)[0] == '0': - return - time.sleep(1) + def __init__( + self, shell_apk_path, local_dir=None, adb_path="adb", target_device=None): + self.shell_apk_path = shell_apk_path + self.adb_path = adb_path + self.local_dir = local_dir + self.target_device = target_device + + def _CreateADBCommand(self, args): + adb_command = [self.adb_path] + if self.target_device: + adb_command.extend(['-s', self.target_device]) + adb_command.extend(args) + return adb_command + + def _ReadFifo(self, fifo_path, pipe, on_fifo_closed, max_attempts=5): + """ + Reads |fifo_path| on the device and write the contents to |pipe|. Calls + |on_fifo_closed| when the fifo is closed. This method will try to find the + path up to |max_attempts|, waiting 1 second between each attempt. If it + cannot find |fifo_path|, a exception will be raised. + """ + fifo_command = self._CreateADBCommand( + ['shell', 'test -e "%s"; echo $?' % fifo_path]) + + def Run(): + def _WaitForFifo(): + for _ in xrange(max_attempts): + if subprocess.check_output(fifo_command)[0] == '0': + return + time.sleep(1) + if on_fifo_closed: + on_fifo_closed() + raise Exception("Unable to find fifo.") + _WaitForFifo() + stdout_cat = subprocess.Popen(self._CreateADBCommand([ + 'shell', + 'cat', + fifo_path]), + stdout=pipe) + atexit.register(_ExitIfNeeded, stdout_cat) + stdout_cat.wait() if on_fifo_closed: on_fifo_closed() - raise Exception("Unable to find fifo.") - _WaitForFifo() - stdout_cat = subprocess.Popen([ADB_PATH, - 'shell', - 'cat', - fifo_path], - stdout=pipe) - atexit.register(_ExitIfNeeded, stdout_cat) - stdout_cat.wait() - if on_fifo_closed: - on_fifo_closed() - thread = threading.Thread(target=Run, name="StdoutRedirector") - thread.start() + thread = threading.Thread(target=Run, name="StdoutRedirector") + thread.start() + def _MapPort(self, device_port, host_port): + """ + Maps the device port to the host port. If |device_port| is 0, a random + available port is chosen. Returns the device port. + """ + def _FindAvailablePortOnDevice(): + opened = subprocess.check_output( + self._CreateADBCommand(['shell', 'netstat'])) + opened = [int(x.strip().split()[3].split(':')[1]) + for x in opened if x.startswith(' tcp')] + while True: + port = random.randint(4096, 16384) + if port not in opened: + return port + if device_port == 0: + device_port = _FindAvailablePortOnDevice() + subprocess.Popen(self._CreateADBCommand([ + "reverse", + "tcp:%d" % device_port, + "tcp:%d" % host_port])).wait() -def _MapPort(device_port, host_port): - """ - Maps the device port to the host port. If |device_port| is 0, a random - available port is chosen. Returns the device port. - """ - def _FindAvailablePortOnDevice(): - opened = subprocess.check_output([ADB_PATH, 'shell', 'netstat']) - opened = [int(x.strip().split()[3].split(':')[1]) - for x in opened if x.startswith(' tcp')] - while True: - port = random.randint(4096, 16384) - if port not in opened: - return port - if device_port == 0: - device_port = _FindAvailablePortOnDevice() - subprocess.Popen([ADB_PATH, - "reverse", - "tcp:%d" % device_port, - "tcp:%d" % host_port]).wait() - def _UnmapPort(): - subprocess.Popen([ADB_PATH, "reverse", "--remove", "tcp:%d" % device_port]) - atexit.register(_UnmapPort) - return device_port + unmap_command = self._CreateADBCommand(["reverse", "--remove", + "tcp:%d" % device_port]) + def _UnmapPort(): + subprocess.Popen(unmap_command) + atexit.register(_UnmapPort) + return device_port -def StartHttpServerForDirectory(path): - """Starts an http server serving files from |path|. Returns the local url.""" - print 'starting http for', path - httpd = _SilentTCPServer(('127.0.0.1', 0), _GetHandlerClassForPath(path)) - atexit.register(httpd.shutdown) + def _StartHttpServerForDirectory(self, path, port=0): + """Starts an http server serving files from |path|. Returns the local + url.""" + assert path + print 'starting http for', path + httpd = _SilentTCPServer(('127.0.0.1', 0), _GetHandlerClassForPath(path)) + atexit.register(httpd.shutdown) - http_thread = threading.Thread(target=httpd.serve_forever) - http_thread.daemon = True - http_thread.start() + http_thread = threading.Thread(target=httpd.serve_forever) + http_thread.daemon = True + http_thread.start() - print 'local port=', httpd.server_address[1] - return 'http://127.0.0.1:%d/' % _MapPort(0, httpd.server_address[1]) + print 'local port=%d' % httpd.server_address[1] + return 'http://127.0.0.1:%d/' % self._MapPort(port, httpd.server_address[1]) + def _StartHttpServerForOriginMapping(self, mapping, port): + """If |mapping| points at a local file starts an http server to serve files + from the directory and returns the new mapping. -def PrepareShellRun(config, origin=None): - """ Prepares for StartShell: runs adb as root and installs the apk. If no - --origin is specified, local http server will be set up to serve files from - the build directory along with port forwarding. + This is intended to be called for every --map-origin value.""" + parts = mapping.split('=') + if len(parts) != 2: + return mapping + dest = parts[1] + # If the destination is a url, don't map it. + if urlparse.urlparse(dest)[0]: + return mapping + # Assume the destination is a local file. Start a local server that + # redirects to it. + localUrl = self._StartHttpServerForDirectory(dest, port) + print 'started server at %s for %s' % (dest, localUrl) + return parts[0] + '=' + localUrl - Returns arguments that should be appended to shell argument list.""" - build_dir = Paths(config).build_dir + def _StartHttpServerForOriginMappings(self, map_parameters, fixed_port): + """Calls _StartHttpServerForOriginMapping for every --map-origin + argument.""" + if not map_parameters: + return [] - subprocess.check_call([ADB_PATH, 'root']) - apk_path = os.path.join(build_dir, 'apks', 'MojoShell.apk') - subprocess.check_call( - [ADB_PATH, 'install', '-r', apk_path, '-i', MOJO_SHELL_PACKAGE_NAME]) - atexit.register(StopShell) + original_values = list(itertools.chain( + *map(lambda x: x[len(MAPPING_PREFIX):].split(','), map_parameters))) + sorted(original_values) + result = [] + for i, value in enumerate(original_values): + result.append(self._StartHttpServerForOriginMapping( + value, DEFAULT_BASE_PORT + 1 + i if fixed_port else 0)) + return [MAPPING_PREFIX + ','.join(result)] - extra_shell_args = [] - origin_url = origin if origin else StartHttpServerForDirectory(build_dir) - extra_shell_args.append("--origin=" + origin_url) + def PrepareShellRun(self, origin=None, fixed_port=True): + """ Prepares for StartShell: runs adb as root and installs the apk. If no + --origin is specified, local http server will be set up to serve files from + the build directory along with port forwarding. - return extra_shell_args + Returns arguments that should be appended to shell argument list.""" + if 'cannot run as root' in subprocess.check_output( + self._CreateADBCommand(['root'])): + raise Exception("Unable to run adb as root.") + subprocess.check_call( + self._CreateADBCommand(['install', '-r', self.shell_apk_path, '-i', + MOJO_SHELL_PACKAGE_NAME])) + atexit.register(self.StopShell) + extra_shell_args = [] + origin_url = origin if origin else self._StartHttpServerForDirectory( + self.local_dir, DEFAULT_BASE_PORT if fixed_port else 0) + extra_shell_args.append("--origin=" + origin_url) -def _StartHttpServerForOriginMapping(mapping): - """If |mapping| points at a local file starts an http server to serve files - from the directory and returns the new mapping. + return extra_shell_args - This is intended to be called for every --map-origin value.""" - parts = mapping.split('=') - if len(parts) != 2: - return mapping - dest = parts[1] - # If the destination is a url, don't map it. - if urlparse.urlparse(dest)[0]: - return mapping - # Assume the destination is a local file. Start a local server that redirects - # to it. - localUrl = StartHttpServerForDirectory(dest) - print 'started server at %s for %s' % (dest, localUrl) - return parts[0] + '=' + localUrl + def StartShell(self, + arguments, + stdout=None, + on_application_stop=None, + fixed_port=True): + """ + Starts the mojo shell, passing it the given arguments. + The |arguments| list must contain the "--origin=" arg from PrepareShellRun. + If |stdout| is not None, it should be a valid argument for subprocess.Popen. + """ + STDOUT_PIPE = "/data/data/%s/stdout.fifo" % MOJO_SHELL_PACKAGE_NAME -def _StartHttpServerForOriginMappings(arg): - """Calls _StartHttpServerForOriginMapping for every --map-origin argument.""" - mapping_prefix = '--map-origin=' - if not arg.startswith(mapping_prefix): - return arg - return mapping_prefix + ','.join([_StartHttpServerForOriginMapping(value) - for value in arg[len(mapping_prefix):].split(',')]) + cmd = self._CreateADBCommand([ + 'shell', + 'am', + 'start', + '-S', + '-a', 'android.intent.action.VIEW', + '-n', '%s/.MojoShellActivity' % MOJO_SHELL_PACKAGE_NAME]) + parameters = [] + if stdout or on_application_stop: + subprocess.check_call(self._CreateADBCommand( + ['shell', 'rm', STDOUT_PIPE])) + parameters.append('--fifo-path=%s' % STDOUT_PIPE) + self._ReadFifo(STDOUT_PIPE, stdout, on_application_stop) + # The origin has to be specified whether it's local or external. + assert any("--origin=" in arg for arg in arguments) -def StartShell(arguments, stdout=None, on_application_stop=None): - """ - Starts the mojo shell, passing it the given arguments. + # Extract map-origin arguments. + map_parameters, other_parameters = _Split(arguments, _IsMapOrigin) + parameters += other_parameters + parameters += self._StartHttpServerForOriginMappings(map_parameters, + fixed_port) - The |arguments| list must contain the "--origin=" arg from PrepareShellRun. - If |stdout| is not None, it should be a valid argument for subprocess.Popen. - """ - STDOUT_PIPE = "/data/data/%s/stdout.fifo" % MOJO_SHELL_PACKAGE_NAME + if parameters: + encodedParameters = json.dumps(parameters) + cmd += ['--es', 'encodedParameters', encodedParameters] - cmd = [ADB_PATH, - 'shell', - 'am', - 'start', - '-W', - '-S', - '-a', 'android.intent.action.VIEW', - '-n', '%s/.MojoShellActivity' % MOJO_SHELL_PACKAGE_NAME] + with open(os.devnull, 'w') as devnull: + subprocess.Popen(cmd, stdout=devnull).wait() - parameters = [] - if stdout or on_application_stop: - subprocess.check_call([ADB_PATH, 'shell', 'rm', STDOUT_PIPE]) - parameters.append('--fifo-path=%s' % STDOUT_PIPE) - _ReadFifo(STDOUT_PIPE, stdout, on_application_stop) - # The origin has to be specified whether it's local or external. - assert any("--origin=" in arg for arg in arguments) - parameters += [_StartHttpServerForOriginMappings(arg) for arg in arguments] + def StopShell(self): + """ + Stops the mojo shell. + """ + subprocess.check_call(self._CreateADBCommand(['shell', + 'am', + 'force-stop', + MOJO_SHELL_PACKAGE_NAME])) - if parameters: - encodedParameters = json.dumps(parameters) - cmd += [ '--es', 'encodedParameters', encodedParameters] + def CleanLogs(self): + """ + Cleans the logs on the device. + """ + subprocess.check_call(self._CreateADBCommand(['logcat', '-c'])) - with open(os.devnull, 'w') as devnull: - subprocess.Popen(cmd, stdout=devnull).wait() + def ShowLogs(self): + """ + Displays the log for the mojo shell. - -def StopShell(): - """ - Stops the mojo shell. - """ - subprocess.check_call( - [ADB_PATH, 'shell', 'am', 'force-stop', MOJO_SHELL_PACKAGE_NAME]) - - -def CleanLogs(): - """ - Cleans the logs on the device. - """ - subprocess.check_call([ADB_PATH, 'logcat', '-c']) - - -def ShowLogs(): - """ - Displays the log for the mojo shell. - - Returns the process responsible for reading the logs. - """ - logcat = subprocess.Popen([ADB_PATH, - 'logcat', - '-s', - ' '.join(LOGCAT_TAGS)], - stdout=sys.stdout) - atexit.register(_ExitIfNeeded, logcat) - return logcat + Returns the process responsible for reading the logs. + """ + logcat = subprocess.Popen(self._CreateADBCommand([ + 'logcat', + '-s', + ' '.join(LOGCAT_TAGS)]), + stdout=sys.stdout) + atexit.register(_ExitIfNeeded, logcat) + return logcat
diff --git a/mojo/tools/mopy/config.py b/mojo/tools/mopy/config.py index 7c8cadd..796a328 100644 --- a/mojo/tools/mopy/config.py +++ b/mojo/tools/mopy/config.py
@@ -6,8 +6,6 @@ "defines" the schema and provides some wrappers.""" -import json -import os.path import platform import sys
diff --git a/mojo/tools/mopy/dart_apptest.py b/mojo/tools/mopy/dart_apptest.py new file mode 100644 index 0000000..5e12b174 --- /dev/null +++ b/mojo/tools/mopy/dart_apptest.py
@@ -0,0 +1,38 @@ +# 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. + +import logging + +_logging = logging.getLogger() + +from mopy import test_util +from mopy.print_process_error import print_process_error + + +# TODO(erg): Support android, launched services and fixture isolation. +def run_test(config, shell, apptest_dict, shell_args, apps_and_args=None): + """Runs a command line and checks the output for signs of gtest failure. + + Args: + config: The mopy.config.Config object for the build. + shell_args: The arguments for mojo_shell. + apps_and_args: A Dict keyed by application URL associated to the + application's specific arguments. + """ + apps_and_args = apps_and_args or {} + output = test_util.try_run_test(config, shell, shell_args, apps_and_args) + # Fail on output with dart unittests' "FAIL:"/"ERROR:" or a lack of "PASS:". + # The latter condition ensures failure on broken command lines or output. + # Check output instead of exit codes because mojo_shell always exits with 0. + if (not output or + '\nFAIL: ' in output or + '\nERROR: ' in output or + '\nPASS: ' not in output): + print "Failed test:" + print_process_error( + test_util.build_command_line(config, shell_args, apps_and_args), + output) + return "Failed test(s) in %r" % apptest_dict + _logging.debug("Succeeded with output:\n%s" % output) + return "Succeeded"
diff --git a/mojo/tools/mopy/gn.py b/mojo/tools/mopy/gn.py index 69ddff4f..024e00f 100644 --- a/mojo/tools/mopy/gn.py +++ b/mojo/tools/mopy/gn.py
@@ -132,4 +132,10 @@ key = result.group(1) value = result.group(2) values[key] = ast.literal_eval(TRANSLATIONS.get(value, value)) + + # TODO(msw): Mojo's script uses GN's is_debug arg to determine the build dir. + # It should probably just use the argument build_dir instead. + if not "is_debug" in values: + values["is_debug"] = "Debug" in build_dir + return values
diff --git a/mojo/tools/mopy/gtest.py b/mojo/tools/mopy/gtest.py new file mode 100644 index 0000000..1cde8ece --- /dev/null +++ b/mojo/tools/mopy/gtest.py
@@ -0,0 +1,132 @@ +# 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. + +import logging +import os +import re +import sys + +from mopy import test_util +from mopy.print_process_error import print_process_error + + +_logger = logging.getLogger() + + +def set_color(): + """Run gtests with color if we're on a TTY (and we're not being told + explicitly what to do).""" + if sys.stdout.isatty() and "GTEST_COLOR" not in os.environ: + _logger.debug("Setting GTEST_COLOR=yes") + os.environ["GTEST_COLOR"] = "yes" + +# TODO(vtl): The return value is bizarre. Should just make it either return +# True/False, or a list of failing fixtures. But the dart_apptest runner would +# also need to be updated in the same way. +def run_fixtures(config, shell, apptest_dict, apptest, isolate, test_args, + shell_args): + """Run the gtest fixtures in isolation.""" + + if not isolate: + if not RunApptestInShell(config, shell, apptest, test_args, shell_args): + return "Failed test(s) in %r" % apptest_dict + return "Succeeded" + + # List the apptest fixtures so they can be run independently for isolation. + fixtures = get_fixtures(config, shell, shell_args, apptest) + + if not fixtures: + return "Failed with no tests found." + + apptest_result = "Succeeded" + for fixture in fixtures: + apptest_args = test_args + ["--gtest_filter=%s" % fixture] + success = RunApptestInShell(config, shell, apptest, apptest_args, + shell_args) + + if not success: + apptest_result = "Failed test(s) in %r" % apptest_dict + + return apptest_result + + +def run_test(config, shell, shell_args, apps_and_args=None): + """Runs a command line and checks the output for signs of gtest failure. + + Args: + config: The mopy.config.Config object for the build. + shell_args: The arguments for mojo_shell. + apps_and_args: A Dict keyed by application URL associated to the + application's specific arguments. + """ + apps_and_args = apps_and_args or {} + output = test_util.try_run_test(config, shell, shell_args, apps_and_args) + # Fail on output with gtest's "[ FAILED ]" or a lack of "[ PASSED ]". + # The latter condition ensures failure on broken command lines or output. + # Check output instead of exit codes because mojo_shell always exits with 0. + if (output is None or + (output.find("[ FAILED ]") != -1 or output.find("[ PASSED ]") == -1)): + print "Failed test:" + print_process_error( + test_util.build_command_line(config, shell_args, apps_and_args), + output) + return False + _logger.debug("Succeeded with output:\n%s" % output) + return True + + +def get_fixtures(config, shell, shell_args, apptest): + """Returns the "Test.Fixture" list from an apptest using mojo_shell. + + Tests are listed by running the given apptest in mojo_shell and passing + --gtest_list_tests. The output is parsed and reformatted into a list like + [TestSuite.TestFixture, ... ] + An empty list is returned on failure, with errors logged. + + Args: + config: The mopy.config.Config object for the build. + apptest: The URL of the test application to run. + """ + try: + apps_and_args = {apptest: ["--gtest_list_tests"]} + list_output = test_util.run_test(config, shell, shell_args, apps_and_args) + _logger.debug("Tests listed:\n%s" % list_output) + return _gtest_list_tests(list_output) + except Exception as e: + print "Failed to get test fixtures:" + print_process_error( + test_util.build_command_line(config, shell_args, apps_and_args), e) + return [] + + +def _gtest_list_tests(gtest_list_tests_output): + """Returns a list of strings formatted as TestSuite.TestFixture from the + output of running --gtest_list_tests on a GTEST application.""" + + # Remove log lines. + gtest_list_tests_output = re.sub("^(\[|WARNING: linker:).*\n", + "", + gtest_list_tests_output, + flags=re.MULTILINE) + + if not re.match("^(\w*\.\r?\n( \w*\r?\n)+)+", gtest_list_tests_output): + raise Exception("Unrecognized --gtest_list_tests output:\n%s" % + gtest_list_tests_output) + + output_lines = gtest_list_tests_output.split("\n") + + test_list = [] + for line in output_lines: + if not line: + continue + if line[0] != " ": + suite = line.strip() + continue + test_list.append(suite + line.strip()) + + return test_list + + +def RunApptestInShell(config, shell, application, application_args, shell_args): + return run_test(config, shell, shell_args, {application: application_args})
diff --git a/mojo/tools/mopy/log.py b/mojo/tools/mopy/log.py new file mode 100644 index 0000000..af57232 --- /dev/null +++ b/mojo/tools/mopy/log.py
@@ -0,0 +1,29 @@ +# 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. + +"""Logging utilities, for use with the standard logging module.""" + + +import logging + + +def InitLogging(verbose_count): + """Ensures that the logger (obtained via logging.getLogger(), as usual) is + initialized, with the log level set as appropriate for |verbose_count| + instances of --verbose on the command line.""" + + assert(verbose_count >= 0) + if verbose_count == 0: + level = logging.WARNING + elif verbose_count == 1: + level = logging.INFO + else: # verbose_count >= 2 + level = logging.DEBUG + + logging.basicConfig(format="%(relativeCreated).3f:%(levelname)s:%(message)s") + logger = logging.getLogger() + logger.setLevel(level) + + logger.debug("Initialized logging: verbose_count=%d, level=%d" % + (verbose_count, level))
diff --git a/mojo/tools/mopy/paths.py b/mojo/tools/mopy/paths.py index 49e1c50..60181e89 100644 --- a/mojo/tools/mopy/paths.py +++ b/mojo/tools/mopy/paths.py
@@ -16,6 +16,8 @@ self.src_root = os.path.abspath(os.path.join(__file__, os.pardir, os.pardir, os.pardir, os.pardir)) self.mojo_dir = os.path.join(self.src_root, "mojo") + self.adb_path = os.path.join(self.src_root, 'third_party', 'android_tools', + 'sdk', 'platform-tools', 'adb') if config: self.build_dir = BuildDirectoryForConfig(config, self.src_root) @@ -25,13 +27,11 @@ self.build_dir = None if self.build_dir is not None: - self.mojo_launcher_path = os.path.join(self.build_dir, "mojo_launcher") self.mojo_shell_path = os.path.join(self.build_dir, "mojo_shell") # TODO(vtl): Use the host OS here, since |config| may not be available. # In any case, if the target is Windows, but the host isn't, using # |os.path| isn't correct.... if Config.GetHostOS() == Config.OS_WINDOWS: - self.mojo_launcher_path += ".exe" self.mojo_shell_path += ".exe" if config and config.target_os == Config.OS_ANDROID: self.target_mojo_shell_path = os.path.join(self.build_dir, @@ -40,7 +40,6 @@ else: self.target_mojo_shell_path = self.mojo_shell_path else: - self.mojo_launcher_path = None self.mojo_shell_path = None self.target_mojo_shell_path = None
diff --git a/mojo/tools/mopy/print_process_error.py b/mojo/tools/mopy/print_process_error.py new file mode 100644 index 0000000..ec565d1 --- /dev/null +++ b/mojo/tools/mopy/print_process_error.py
@@ -0,0 +1,22 @@ +# 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. + +def print_process_error(command_line, error): + """Properly format an exception raised from a failed command execution.""" + + if command_line: + print 'Failed command: %r' % command_line + else: + print 'Failed command:' + print 72 * '-' + + if hasattr(error, 'returncode'): + print ' with exit code %d' % error.returncode + print 72 * '-' + + if hasattr(error, 'output'): + print error.output + else: + print error + print 72 * '-'
diff --git a/mojo/tools/mopy/test_util.py b/mojo/tools/mopy/test_util.py new file mode 100644 index 0000000..4a4c128 --- /dev/null +++ b/mojo/tools/mopy/test_util.py
@@ -0,0 +1,95 @@ +# 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 logging +import os +import subprocess +import time + +from mopy.config import Config +from mopy.paths import Paths +from mopy.print_process_error import print_process_error + + +_logger = logging.getLogger() + + +def build_shell_arguments(shell_args, apps_and_args=None): + """Build the list of arguments for the shell. |shell_args| are the base + arguments, |apps_and_args| is a dictionary that associates each application to + its specific arguments|. Each app included will be run by the shell. + """ + result = shell_args[:] + if apps_and_args: + # TODO(msw): Mojo's script uses --args-for; Chromium lacks support for that. + for app_and_args in apps_and_args.items(): + result += app_and_args[1] + result += apps_and_args.keys() + return result + + +def get_shell_executable(config): + paths = Paths(config=config) + if config.target_os == Config.OS_ANDROID: + return os.path.join(paths.src_root, "mojo", "tools", + "android_mojo_shell.py") + else: + return paths.mojo_shell_path + + +def build_command_line(config, shell_args, apps_and_args): + executable = get_shell_executable(config) + return "%s %s" % (executable, " ".join(["%r" % x for x in + build_shell_arguments( + shell_args, apps_and_args)])) + + +def run_test_android(shell, shell_args, apps_and_args): + """Run the given test on the single/default android device.""" + assert shell + (r, w) = os.pipe() + with os.fdopen(r, "r") as rf: + with os.fdopen(w, "w") as wf: + arguments = build_shell_arguments(shell_args, apps_and_args) + _logger.debug("Starting shell with arguments: %s" % arguments) + start_time = time.time() + # TODO(vtl): Do more logging in lower layers. + shell.StartShell(arguments, wf, wf.close, False) + rv = rf.read() + run_time = time.time() - start_time + _logger.debug("Shell completed") + # Only log if it took more than 3 seconds. + if run_time >= 3: + _logger.info("Shell test took %.3f seconds; arguments: %s" % + (run_time, arguments)) + return rv + + +def run_test(config, shell, shell_args, apps_and_args): + """Run the given test.""" + if (config.target_os == Config.OS_ANDROID): + return run_test_android(shell, shell_args, apps_and_args) + + executable = get_shell_executable(config) + command = ([executable] + build_shell_arguments(shell_args, apps_and_args)) + _logger.debug("Starting: %s" % " ".join(command)) + start_time = time.time() + rv = subprocess.check_output(command, stderr=subprocess.STDOUT) + run_time = time.time() - start_time + _logger.debug("Completed: %s" % " ".join(command)) + # Only log if it took more than 1 second. + if run_time >= 1: + _logger.info("Test took %.3f seconds: %s" % (run_time, " ".join(command))) + return rv + + +def try_run_test(config, shell, shell_args, apps_and_args): + """Returns the output of a command line or an empty string on error.""" + command_line = build_command_line(config, shell_args, apps_and_args) + _logger.debug("Running command line: %s" % command_line) + try: + return run_test(config, shell, shell_args, apps_and_args) + except Exception as e: + print_process_error(command_line, e) + return None
diff --git a/mojo/tools/rev_sdk.py b/mojo/tools/rev_sdk.py new file mode 100755 index 0000000..c7508683 --- /dev/null +++ b/mojo/tools/rev_sdk.py
@@ -0,0 +1,69 @@ +#!/usr/bin/env python +# 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. + +"""Tool to roll Mojo into Chromium. See: +https://github.com/domokit/mojo/wiki/Rolling-code-between-chromium-and-mojo#mojo---chromium-updates-sdk--edk +""" + +import os +import sys +from utils import commit +from utils import mojo_root_dir +from utils import system + +sdk_prefix_in_chromium = "third_party/mojo/src" +sdk_dirs_to_clone = [ + "mojo/edk", + "mojo/public", + "nacl_bindings", +] + + +services_prefix_in_mojo = "mojo/services" +services_prefix_in_chromium = "third_party/mojo_services/src" + +# A dictionary mapping dirs to clone to their destination locations in Chromium. +dirs_to_clone = {} + +for sdk_dir in sdk_dirs_to_clone: + sdk_dir_in_chromium = os.path.join(sdk_prefix_in_chromium, sdk_dir) + dirs_to_clone[sdk_dir] = sdk_dir_in_chromium + +def rev(source_dir, chromium_dir): + src_commit = system(["git", "show-ref", "HEAD", "-s"], cwd=source_dir).strip() + + for input_dir, dest_dir in dirs_to_clone.iteritems(): + if os.path.exists(os.path.join(chromium_dir, dest_dir)): + print "removing directory %s" % dest_dir + system(["git", "rm", "-r", dest_dir], cwd=chromium_dir) + print "cloning directory %s into %s" % (input_dir, dest_dir) + files = system(["git", "ls-files", input_dir], cwd=source_dir) + for f in files.splitlines(): + # Don't copy presubmit files over since the code is read-only on the + # chromium side. + if os.path.basename(f) == "PRESUBMIT.py": + continue + + # Clone |f| into Chromium under |dest_dir| at its location relative to + # |input_dir|. + f_relpath = os.path.relpath(f, input_dir) + dest_path = os.path.join(chromium_dir, dest_dir, f_relpath) + system(["mkdir", "-p", os.path.dirname(dest_path)]) + system(["cp", os.path.join(source_dir, f), dest_path]) + os.chdir(chromium_dir) + system(["git", "add", dest_dir], cwd=chromium_dir) + + mojo_public_dest_dir = os.path.join(sdk_prefix_in_chromium, "mojo/public") + version_filename = os.path.join(mojo_public_dest_dir, "VERSION") + with open(version_filename, "w") as version_file: + version_file.write(src_commit) + system(["git", "add", version_filename], cwd=chromium_dir) + commit("Update mojo sdk to rev " + src_commit, cwd=chromium_dir) + +if len(sys.argv) != 2: + print "usage: rev_sdk.py <chromium source dir>" + sys.exit(1) + +rev(mojo_root_dir, sys.argv[1])
diff --git a/native_client_sdk/native_client_sdk_untrusted.gyp b/native_client_sdk/native_client_sdk_untrusted.gyp index 140664f..2cd3dc5 100644 --- a/native_client_sdk/native_client_sdk_untrusted.gyp +++ b/native_client_sdk/native_client_sdk_untrusted.gyp
@@ -35,7 +35,6 @@ '>!@pymod_do_main(dsc_info -s -l src/libraries/nacl_io nacl_io)', ], 'dependencies': [ - '../native_client/tools.gyp:prep_toolchain', '../ppapi/native_client/native_client.gyp:ppapi_lib', ], },
diff --git a/net/BUILD.gn b/net/BUILD.gn index b425371..130a597 100644 --- a/net/BUILD.gn +++ b/net/BUILD.gn
@@ -194,11 +194,7 @@ "cert/ct_objects_extractor_nss.cc", "cert/jwk_serializer_nss.cc", "cert/scoped_nss_types.h", - "cert/test_root_certs_nss.cc", "cert/x509_util_nss.cc", - "cert/x509_util_nss.h", - "cert_net/nss_ocsp.cc", - "cert_net/nss_ocsp.h", "quic/crypto/aead_base_decrypter_nss.cc", "quic/crypto/aead_base_encrypter_nss.cc", "quic/crypto/aes_128_gcm_12_decrypter_nss.cc", @@ -214,31 +210,6 @@ "socket/ssl_server_socket_nss.cc", "socket/ssl_server_socket_nss.h", ] - if (is_chromeos) { - sources -= [ - "cert/nss_cert_database_chromeos.cc", - "cert/nss_cert_database_chromeos.h", - "cert/nss_profile_filter_chromeos.cc", - "cert/nss_profile_filter_chromeos.h", - ] - } - if (is_linux) { - # These are always removed for non-Linux cases below. - sources -= [ - "base/crypto_module_nss.cc", - "base/keygen_handler_nss.cc", - "cert/cert_database_nss.cc", - "cert/nss_cert_database.cc", - "cert/nss_cert_database.h", - "cert/x509_certificate_nss.cc", - "third_party/mozilla_security_manager/nsKeygenHandler.cpp", - "third_party/mozilla_security_manager/nsKeygenHandler.h", - "third_party/mozilla_security_manager/nsNSSCertificateDB.cpp", - "third_party/mozilla_security_manager/nsNSSCertificateDB.h", - "third_party/mozilla_security_manager/nsPKCS12Blob.cpp", - "third_party/mozilla_security_manager/nsPKCS12Blob.h", - ] - } if (is_ios) { # Always removed for !ios below. sources -= [ @@ -249,9 +220,11 @@ if (is_win) { sources -= [ "cert/sha256_legacy_support_nss_win.cc" ] } + if (!use_nss_certs && !is_ios) { + sources -= [ "cert/x509_util_nss.h" ] + } } else { sources -= [ - "base/crypto_module_openssl.cc", "cert/ct_log_verifier_openssl.cc", "cert/ct_objects_extractor_openssl.cc", "cert/jwk_serializer_openssl.cc", @@ -272,6 +245,7 @@ "socket/ssl_server_socket_openssl.cc", "socket/ssl_server_socket_openssl.h", "ssl/openssl_platform_key.h", + "ssl/openssl_platform_key_nss.cc", "ssl/openssl_ssl_util.cc", "ssl/openssl_ssl_util.h", "ssl/ssl_client_session_cache_openssl.cc", @@ -290,6 +264,7 @@ if (!use_openssl_certs) { sources -= [ + "base/crypto_module_openssl.cc", "base/keygen_handler_openssl.cc", "base/openssl_private_key_store.h", "base/openssl_private_key_store_memory.cc", @@ -321,7 +296,9 @@ if (is_linux) { configs += [ "//build/config/linux:libresolv" ] - } else { + } + + if (!use_nss_certs) { sources -= [ "base/crypto_module_nss.cc", "base/keygen_handler_nss.cc", @@ -329,6 +306,8 @@ "cert/nss_cert_database.cc", "cert/nss_cert_database.h", "cert/x509_certificate_nss.cc", + "ssl/client_cert_store_nss.cc", + "ssl/client_cert_store_nss.h", "third_party/mozilla_security_manager/nsKeygenHandler.cpp", "third_party/mozilla_security_manager/nsKeygenHandler.h", "third_party/mozilla_security_manager/nsNSSCertificateDB.cpp", @@ -336,38 +315,37 @@ "third_party/mozilla_security_manager/nsPKCS12Blob.cpp", "third_party/mozilla_security_manager/nsPKCS12Blob.h", ] - - if (!is_ios && !use_openssl) { - # These files are part of the partial implementation of NSS on iOS so - # keep them in that case. - sources -= [ - "cert/test_root_certs_nss.cc", - "cert_net/nss_ocsp.cc", - "cert_net/nss_ocsp.h", - ] - } - } - - if (!use_nss_certs) { - sources -= [ - "ssl/client_cert_store_nss.cc", - "ssl/client_cert_store_nss.h", - ] if (!is_ios) { # These files are part of the partial implementation of NSS on iOS so # keep them in that case (even though use_nss_certs is not set). sources -= [ "cert/cert_verify_proc_nss.cc", "cert/cert_verify_proc_nss.h", + "cert/test_root_certs_nss.cc", + "cert/x509_util_nss_certs.cc", + "cert_net/nss_ocsp.cc", + "cert_net/nss_ocsp.h", ] } if (is_chromeos) { # These were already removed on non-ChromeOS. sources -= [ + "cert/nss_cert_database_chromeos.cc", + "cert/nss_cert_database_chromeos.h", + "cert/nss_profile_filter_chromeos.cc", + "cert/nss_profile_filter_chromeos.h", "ssl/client_cert_store_chromeos.cc", "ssl/client_cert_store_chromeos.h", ] } + if (use_openssl) { + sources -= [ "ssl/openssl_platform_key_nss.cc" ] + } + } else if (use_openssl) { + # client_cert_store_nss.c requires NSS_CmpCertChainWCANames from NSS's + # libssl, but our bundled copy is not built in OpenSSL ports. Pull that file + # in directly. + sources += [ "third_party/nss/ssl/cmpcert.c" ] } if (!enable_websockets) { @@ -460,8 +438,8 @@ } if (is_ios) { - # Add back some sources that were otherwise filtered out. iOS additionally - # doesn't set USE_NSS_CERTS but needs some of the files. + # Add back some sources that were otherwise filtered out. iOS needs some Mac + # files. set_sources_assignment_filter([]) sources += [ "base/net_util_mac.cc", @@ -469,13 +447,6 @@ "base/network_change_notifier_mac.cc", "base/network_config_watcher_mac.cc", "base/platform_mime_util_mac.mm", - "cert/cert_verify_proc_nss.cc", - "cert/cert_verify_proc_nss.h", - "cert/test_root_certs_nss.cc", - "cert/x509_util_nss.cc", - "cert/x509_util_nss.h", - "cert_net/nss_ocsp.cc", - "cert_net/nss_ocsp.h", "proxy/proxy_resolver_mac.cc", "proxy/proxy_server_mac.cc", ] @@ -1317,9 +1288,8 @@ } # TODO(GYP) make this compile on Android, we need some native test deps done. -# TODO(GYP) Also doesn't work on Windows; dependency on boringssl is wrong. # TODO(GYP) Also doesn't work on Mac, need to figure out why not. -if (!is_android && !is_win && !is_mac) { +if (!is_android && !is_mac) { test("net_unittests") { sources = gypi_values.net_test_sources @@ -1328,11 +1298,11 @@ defines = [] deps = [ + ":balsa", ":extras", ":http_server", ":net", ":net_quic_proto", - ":epoll_quic_tools", ":simple_quic_tools", ":test_support", "//base", @@ -1352,6 +1322,9 @@ "//url", ] + if (is_desktop_linux) { + deps += [ ":epoll_quic_tools" ] + } if (is_linux) { sources += gypi_values.net_linux_test_sources deps += [ @@ -1390,9 +1363,16 @@ } if (!use_nss_certs) { - sources -= [ "ssl/client_cert_store_nss_unittest.cc" ] + sources -= [ + "cert/nss_cert_database_unittest.cc", + "ssl/client_cert_store_nss_unittest.cc", + ] if (is_chromeos) { # Already removed for all non-ChromeOS builds. - sources -= [ "ssl/client_cert_store_chromeos_unittest.cc" ] + sources -= [ + "cert/nss_cert_database_chromeos_unittest.cc", + "cert/nss_profile_filter_chromeos_unittest.cc", + "ssl/client_cert_store_chromeos_unittest.cc", + ] } } @@ -1402,17 +1382,9 @@ # TODO(bulach): Add equivalent tests when the underlying # functionality is ported to OpenSSL. sources -= [ - "cert/nss_cert_database_unittest.cc", "cert/x509_util_nss_unittest.cc", "quic/test_tools/crypto_test_utils_nss.cc", ] - if (is_chromeos) { - # These were already removed in the non-ChromeOS case. - sources -= [ - "cert/nss_cert_database_chromeos_unittest.cc", - "cert/nss_profile_filter_chromeos_unittest.cc", - ] - } } else { sources -= [ "cert/x509_util_openssl_unittest.cc", @@ -1420,9 +1392,6 @@ "socket/ssl_client_socket_openssl_unittest.cc", "ssl/ssl_client_session_cache_openssl_unittest.cc", ] - if (!is_desktop_linux && !is_chromeos) { - sources -= [ "cert/nss_cert_database_unittest.cc" ] - } } if (use_kerberos) {
diff --git a/net/base/address_list.cc b/net/base/address_list.cc index 02a762bf..a62b5d05 100644 --- a/net/base/address_list.cc +++ b/net/base/address_list.cc
@@ -15,7 +15,7 @@ namespace { base::Value* NetLogAddressListCallback(const AddressList* address_list, - NetLog::LogLevel log_level) { + NetLogCaptureMode capture_mode) { base::DictionaryValue* dict = new base::DictionaryValue(); base::ListValue* list = new base::ListValue();
diff --git a/net/base/elements_upload_data_stream_unittest.cc b/net/base/elements_upload_data_stream_unittest.cc index e2a431c..78229d2 100644 --- a/net/base/elements_upload_data_stream_unittest.cc +++ b/net/base/elements_upload_data_stream_unittest.cc
@@ -64,13 +64,13 @@ init_result_(OK), read_result_(OK) {} - virtual ~MockUploadElementReader() {} + ~MockUploadElementReader() override {} // UploadElementReader overrides. MOCK_METHOD1(Init, int(const CompletionCallback& callback)); - virtual uint64 GetContentLength() const override { return content_length_; } - virtual uint64 BytesRemaining() const override { return bytes_remaining_; } - virtual bool IsInMemory() const override { return is_in_memory_; } + uint64 GetContentLength() const override { return content_length_; } + uint64 BytesRemaining() const override { return bytes_remaining_; } + bool IsInMemory() const override { return is_in_memory_; } MOCK_METHOD3(Read, int(IOBuffer* buf, int buf_length, const CompletionCallback& callback));
diff --git a/net/base/network_change_notifier_win_unittest.cc b/net/base/network_change_notifier_win_unittest.cc index 25873c3..ca59750 100644 --- a/net/base/network_change_notifier_win_unittest.cc +++ b/net/base/network_change_notifier_win_unittest.cc
@@ -24,15 +24,15 @@ public: TestNetworkChangeNotifierWin() {} - virtual ~TestNetworkChangeNotifierWin() { + ~TestNetworkChangeNotifierWin() override { // This is needed so we don't try to stop watching for IP address changes, // as we never actually started. set_is_watching(false); } // From NetworkChangeNotifierWin. - virtual NetworkChangeNotifier::ConnectionType - RecomputeCurrentConnectionType() const override { + NetworkChangeNotifier::ConnectionType RecomputeCurrentConnectionType() + const override { return NetworkChangeNotifier::CONNECTION_UNKNOWN; }
diff --git a/net/base/sdch_net_log_params.cc b/net/base/sdch_net_log_params.cc index 02e0ad78..a89117ec 100644 --- a/net/base/sdch_net_log_params.cc +++ b/net/base/sdch_net_log_params.cc
@@ -11,7 +11,7 @@ namespace net { base::Value* NetLogSdchResourceProblemCallback(SdchProblemCode problem, - NetLog::LogLevel log_level) { + NetLogCaptureMode capture_mode) { base::DictionaryValue* dict = new base::DictionaryValue(); dict->SetInteger("sdch_problem_code", problem); dict->SetInteger("net_error", ERR_FAILED); @@ -22,7 +22,7 @@ SdchProblemCode problem, const GURL& url, bool is_error, - NetLog::LogLevel log_level) { + NetLogCaptureMode capture_mode) { base::DictionaryValue* dict = new base::DictionaryValue(); dict->SetInteger("sdch_problem_code", problem); dict->SetString("dictionary_url", url.spec());
diff --git a/net/base/sdch_net_log_params.h b/net/base/sdch_net_log_params.h index 1849f96..a13d9de 100644 --- a/net/base/sdch_net_log_params.h +++ b/net/base/sdch_net_log_params.h
@@ -17,7 +17,7 @@ NET_EXPORT base::Value* NetLogSdchResourceProblemCallback( SdchProblemCode problem, - NetLog::LogLevel log_level); + NetLogCaptureMode capture_mode); // If |is_error| is false, "net_error" field won't be added to the JSON and the // event won't be painted red in the netlog. @@ -25,7 +25,7 @@ SdchProblemCode problem, const GURL& url, bool is_error, - NetLog::LogLevel log_level); + NetLogCaptureMode capture_mode); } // namespace net
diff --git a/net/cert/cert_policy_enforcer.cc b/net/cert/cert_policy_enforcer.cc index d4bae50..4c6d38d2 100644 --- a/net/cert/cert_policy_enforcer.cc +++ b/net/cert/cert_policy_enforcer.cc
@@ -185,11 +185,12 @@ base::Version whitelist_version; }; -base::Value* NetLogComplianceCheckResultCallback(X509Certificate* cert, - ComplianceDetails* details, - NetLog::LogLevel log_level) { +base::Value* NetLogComplianceCheckResultCallback( + X509Certificate* cert, + ComplianceDetails* details, + NetLogCaptureMode capture_mode) { base::DictionaryValue* dict = new base::DictionaryValue(); - dict->Set("certificate", NetLogX509CertificateCallback(cert, log_level)); + dict->Set("certificate", NetLogX509CertificateCallback(cert, capture_mode)); dict->SetBoolean("policy_enforcement_required", details->ct_presence_required); if (details->ct_presence_required) {
diff --git a/net/cert/ct_known_logs_static.h b/net/cert/ct_known_logs_static.h index 25356520..0163caa 100644 --- a/net/cert/ct_known_logs_static.h +++ b/net/cert/ct_known_logs_static.h
@@ -12,48 +12,49 @@ }; const CTLogInfo kCTLogList[] = { - {"\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\x7d\xa8\x4b\x12\x29\x80\xa3" - "\x3d\xad\xd3\x5a\x77\xb8\xcc\xe2\x88\xb3\xa5\xfd\xf1\xd3\x0c\xcd\x18" - "\x0c\xe8\x41\x46\xe8\x81\x01\x1b\x15\xe1\x4b\xf1\x1b\x62\xdd\x36\x0a" - "\x08\x18\xba\xed\x0b\x35\x84\xd0\x9e\x40\x3c\x2d\x9e\x9b\x82\x65\xbd" - "\x1f\x04\x10\x41\x4c\xa0", - "Google 'Pilot' log" - }, - {"\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\xd7\xf4\xcc\x69\xb2\xe4\x0e" - "\x90\xa3\x8a\xea\x5a\x70\x09\x4f\xef\x13\x62\xd0\x8d\x49\x60\xff\x1b" - "\x40\x50\x07\x0c\x6d\x71\x86\xda\x25\x49\x8d\x65\xe1\x08\x0d\x47\x34" - "\x6b\xbd\x27\xbc\x96\x21\x3e\x34\xf5\x87\x76\x31\xb1\x7f\x1d\xc9\x85" - "\x3b\x0d\xf7\x1f\x3f\xe9", - "Google 'Aviator' log" - }, - {"\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\x02\x46\xc5\xbe\x1b\xbb\x82" - "\x40\x16\xe8\xc1\xd2\xac\x19\x69\x13\x59\xf8\xf8\x70\x85\x46\x40\xb9" - "\x38\xb0\x23\x82\xa8\x64\x4c\x7f\xbf\xbb\x34\x9f\x4a\x5f\x28\x8a\xcf" - "\x19\xc4\x00\xf6\x36\x06\x93\x65\xed\x4c\xf5\xa9\x21\x62\x5a\xd8\x91" - "\xeb\x38\x24\x40\xac\xe8", - "DigiCert Log Server" - }, - {"\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\x20\x5b\x18\xc8\x3c\xc1\x8b" - "\xb3\x31\x08\x00\xbf\xa0\x90\x57\x2b\xb7\x47\x8c\x6f\xb5\x68\xb0\x8e" - "\x90\x78\xe9\xa0\x73\xea\x4f\x28\x21\x2e\x9c\xc0\xf4\x16\x1b\xaa\xf9" - "\xd5\xd7\xa9\x80\xc3\x4e\x2f\x52\x3c\x98\x01\x25\x46\x24\x25\x28\x23" - "\x77\x2d\x05\xc2\x40\x7a", - "Google 'Rocketeer' log" - }, - {"\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\x0b\x23\xcb\x85\x62\x98\x61" - "\x48\x04\x73\xeb\x54\x5d\xf3\xd0\x07\x8c\x2d\x19\x2d\x8c\x36\xf5\xeb" - "\x8f\x01\x42\x0a\x7c\x98\x26\x27\xc1\xb5\xdd\x92\x93\xb0\xae\xf8\x9b" - "\x3d\x0c\xd8\x4c\x4e\x1d\xf9\x15\xfb\x47\x68\x7b\xba\x66\xb7\x25\x9c" - "\xd0\x4a\xc2\x66\xdb\x48", - "Certly.IO log" - } -}; + {"\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\x7d\xa8\x4b\x12\x29\x80\xa3" + "\x3d\xad\xd3\x5a\x77\xb8\xcc\xe2\x88\xb3\xa5\xfd\xf1\xd3\x0c\xcd\x18" + "\x0c\xe8\x41\x46\xe8\x81\x01\x1b\x15\xe1\x4b\xf1\x1b\x62\xdd\x36\x0a" + "\x08\x18\xba\xed\x0b\x35\x84\xd0\x9e\x40\x3c\x2d\x9e\x9b\x82\x65\xbd" + "\x1f\x04\x10\x41\x4c\xa0", + "Google 'Pilot' log"}, + {"\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\xd7\xf4\xcc\x69\xb2\xe4\x0e" + "\x90\xa3\x8a\xea\x5a\x70\x09\x4f\xef\x13\x62\xd0\x8d\x49\x60\xff\x1b" + "\x40\x50\x07\x0c\x6d\x71\x86\xda\x25\x49\x8d\x65\xe1\x08\x0d\x47\x34" + "\x6b\xbd\x27\xbc\x96\x21\x3e\x34\xf5\x87\x76\x31\xb1\x7f\x1d\xc9\x85" + "\x3b\x0d\xf7\x1f\x3f\xe9", + "Google 'Aviator' log"}, + {"\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\x02\x46\xc5\xbe\x1b\xbb\x82" + "\x40\x16\xe8\xc1\xd2\xac\x19\x69\x13\x59\xf8\xf8\x70\x85\x46\x40\xb9" + "\x38\xb0\x23\x82\xa8\x64\x4c\x7f\xbf\xbb\x34\x9f\x4a\x5f\x28\x8a\xcf" + "\x19\xc4\x00\xf6\x36\x06\x93\x65\xed\x4c\xf5\xa9\x21\x62\x5a\xd8\x91" + "\xeb\x38\x24\x40\xac\xe8", + "DigiCert Log Server"}, + {"\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\x20\x5b\x18\xc8\x3c\xc1\x8b" + "\xb3\x31\x08\x00\xbf\xa0\x90\x57\x2b\xb7\x47\x8c\x6f\xb5\x68\xb0\x8e" + "\x90\x78\xe9\xa0\x73\xea\x4f\x28\x21\x2e\x9c\xc0\xf4\x16\x1b\xaa\xf9" + "\xd5\xd7\xa9\x80\xc3\x4e\x2f\x52\x3c\x98\x01\x25\x46\x24\x25\x28\x23" + "\x77\x2d\x05\xc2\x40\x7a", + "Google 'Rocketeer' log"}, + {"\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\x0b\x23\xcb\x85\x62\x98\x61" + "\x48\x04\x73\xeb\x54\x5d\xf3\xd0\x07\x8c\x2d\x19\x2d\x8c\x36\xf5\xeb" + "\x8f\x01\x42\x0a\x7c\x98\x26\x27\xc1\xb5\xdd\x92\x93\xb0\xae\xf8\x9b" + "\x3d\x0c\xd8\x4c\x4e\x1d\xf9\x15\xfb\x47\x68\x7b\xba\x66\xb7\x25\x9c" + "\xd0\x4a\xc2\x66\xdb\x48", + "Certly.IO log"}, + {"\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\x27\x64\x39\x0c\x2d\xdc\x50" + "\x18\xf8\x21\x00\xa2\x0e\xed\x2c\xea\x3e\x75\xba\x9f\x93\x64\x09\x00" + "\x11\xc4\x11\x17\xab\x5c\xcf\x0f\x74\xac\xb5\x97\x90\x93\x00\x5b\xb8" + "\xeb\xf7\x27\x3d\xd9\xb2\x0a\x81\x5f\x2f\x0d\x75\x38\x94\x37\x99\x1e" + "\xf6\x07\x76\xe0\xee\xbe", + "Izenpe log"}}; -const size_t kNumKnownCTLogs = 5; +const size_t kNumKnownCTLogs = 6; #endif // NET_CERT_CT_KNOWN_LOGS_STATIC_H_
diff --git a/net/cert/ct_signed_certificate_timestamp_log_param.cc b/net/cert/ct_signed_certificate_timestamp_log_param.cc index e7bb97c..71c8469f 100644 --- a/net/cert/ct_signed_certificate_timestamp_log_param.cc +++ b/net/cert/ct_signed_certificate_timestamp_log_param.cc
@@ -129,7 +129,8 @@ } // namespace base::Value* NetLogSignedCertificateTimestampCallback( - const ct::CTVerifyResult* ct_result, NetLog::LogLevel log_level) { + const ct::CTVerifyResult* ct_result, + NetLogCaptureMode capture_mode) { base::DictionaryValue* dict = new base::DictionaryValue(); dict->Set("verified_scts", @@ -148,7 +149,7 @@ const std::string* embedded_scts, const std::string* sct_list_from_ocsp, const std::string* sct_list_from_tls_extension, - NetLog::LogLevel log_level) { + NetLogCaptureMode capture_mode) { base::DictionaryValue* dict = new base::DictionaryValue(); SetBinaryData("embedded_scts", *embedded_scts, dict);
diff --git a/net/cert/ct_signed_certificate_timestamp_log_param.h b/net/cert/ct_signed_certificate_timestamp_log_param.h index 5c4db191..25e1fb5 100644 --- a/net/cert/ct_signed_certificate_timestamp_log_param.h +++ b/net/cert/ct_signed_certificate_timestamp_log_param.h
@@ -18,7 +18,8 @@ // See the documentation for SIGNED_CERTIFICATE_TIMESTAMPS_CHECKED // in net/log/net_log_event_type_list.h base::Value* NetLogSignedCertificateTimestampCallback( - const ct::CTVerifyResult* ct_result, NetLog::LogLevel log_level); + const ct::CTVerifyResult* ct_result, + NetLogCaptureMode capture_mode); // Creates a dictionary of raw Signed Certificate Timestamps to be logged // in the NetLog. @@ -28,7 +29,7 @@ const std::string* embedded_scts, const std::string* sct_list_from_ocsp, const std::string* sct_list_from_tls_extension, - NetLog::LogLevel log_level); + NetLogCaptureMode capture_mode); } // namespace net
diff --git a/net/cert/ev_root_ca_metadata_unittest.cc b/net/cert/ev_root_ca_metadata_unittest.cc index 39699e26..9da0064 100644 --- a/net/cert/ev_root_ca_metadata_unittest.cc +++ b/net/cert/ev_root_ca_metadata_unittest.cc
@@ -9,6 +9,7 @@ #include "testing/gtest/include/gtest/gtest.h" #if defined(USE_NSS_CERTS) +#include "crypto/nss_util.h" #include "crypto/scoped_nss_types.h" #endif @@ -63,6 +64,7 @@ } bool EVOidData::Init() { + crypto::EnsureNSSInit(); crypto::ScopedPLArenaPool pool(PORT_NewArena(DER_DEFAULT_CHUNKSIZE)); if (!pool.get()) return false;
diff --git a/net/cert/multi_threaded_cert_verifier.cc b/net/cert/multi_threaded_cert_verifier.cc index 9e78abb..128987f 100644 --- a/net/cert/multi_threaded_cert_verifier.cc +++ b/net/cert/multi_threaded_cert_verifier.cc
@@ -82,7 +82,7 @@ const unsigned kTTLSecs = 1800; // 30 minutes. base::Value* CertVerifyResultCallback(const CertVerifyResult& verify_result, - NetLog::LogLevel log_level) { + NetLogCaptureMode capture_mode) { base::DictionaryValue* results = new base::DictionaryValue(); results->SetBoolean("has_md5", verify_result.has_md5); results->SetBoolean("has_md2", verify_result.has_md2); @@ -96,7 +96,7 @@ results->SetInteger("cert_status", verify_result.cert_status); results->Set("verified_cert", NetLogX509CertificateCallback(verify_result.verified_cert.get(), - log_level)); + capture_mode)); base::ListValue* hashes = new base::ListValue(); for (std::vector<HashValue>::const_iterator it =
diff --git a/net/cert/x509_certificate_net_log_param.cc b/net/cert/x509_certificate_net_log_param.cc index 3c52b96..a9445c6 100644 --- a/net/cert/x509_certificate_net_log_param.cc +++ b/net/cert/x509_certificate_net_log_param.cc
@@ -13,7 +13,7 @@ namespace net { base::Value* NetLogX509CertificateCallback(const X509Certificate* certificate, - NetLog::LogLevel log_level) { + NetLogCaptureMode capture_mode) { base::DictionaryValue* dict = new base::DictionaryValue(); base::ListValue* certs = new base::ListValue(); std::vector<std::string> encoded_chain;
diff --git a/net/cert/x509_certificate_net_log_param.h b/net/cert/x509_certificate_net_log_param.h index 822cb53..9e3e3a9 100644 --- a/net/cert/x509_certificate_net_log_param.h +++ b/net/cert/x509_certificate_net_log_param.h
@@ -12,9 +12,8 @@ class X509Certificate; // Creates NetLog parameter to describe an X509Certificate. -base::Value* NetLogX509CertificateCallback( - const X509Certificate* certificate, - NetLog::LogLevel log_level); +base::Value* NetLogX509CertificateCallback(const X509Certificate* certificate, + NetLogCaptureMode capture_mode); } // namespace net
diff --git a/net/cert/x509_util_nss.cc b/net/cert/x509_util_nss.cc index 9711ef6..ecdf08b 100644 --- a/net/cert/x509_util_nss.cc +++ b/net/cert/x509_util_nss.cc
@@ -194,58 +194,6 @@ return true; } -#if defined(USE_NSS_CERTS) || defined(OS_IOS) -// Callback for CERT_DecodeCertPackage(), used in -// CreateOSCertHandlesFromBytes(). -SECStatus PR_CALLBACK CollectCertsCallback(void* arg, - SECItem** certs, - int num_certs) { - X509Certificate::OSCertHandles* results = - reinterpret_cast<X509Certificate::OSCertHandles*>(arg); - - for (int i = 0; i < num_certs; ++i) { - X509Certificate::OSCertHandle handle = - X509Certificate::CreateOSCertHandleFromBytes( - reinterpret_cast<char*>(certs[i]->data), certs[i]->len); - if (handle) - results->push_back(handle); - } - - return SECSuccess; -} - -typedef scoped_ptr< - CERTName, - crypto::NSSDestroyer<CERTName, CERT_DestroyName> > ScopedCERTName; - -// Create a new CERTName object from its encoded representation. -// |arena| is the allocation pool to use. -// |data| points to a DER-encoded X.509 DistinguishedName. -// Return a new CERTName pointer on success, or NULL. -CERTName* CreateCertNameFromEncoded(PLArenaPool* arena, - const base::StringPiece& data) { - if (!arena) - return NULL; - - ScopedCERTName name(PORT_ArenaZNew(arena, CERTName)); - if (!name.get()) - return NULL; - - SECItem item; - item.len = static_cast<unsigned int>(data.length()); - item.data = reinterpret_cast<unsigned char*>( - const_cast<char*>(data.data())); - - SECStatus rv = SEC_ASN1DecodeItem( - arena, name.get(), SEC_ASN1_GET(CERT_NameTemplate), &item); - if (rv != SECSuccess) - return NULL; - - return name.release(); -} - -#endif // defined(USE_NSS_CERTS) || defined(OS_IOS) - } // namespace namespace x509_util { @@ -368,271 +316,6 @@ return true; } -#if defined(USE_NSS_CERTS) || defined(OS_IOS) -void ParsePrincipal(CERTName* name, CertPrincipal* principal) { -// Starting in NSS 3.15, CERTGetNameFunc takes a const CERTName* argument. -#if NSS_VMINOR >= 15 - typedef char* (*CERTGetNameFunc)(const CERTName* name); -#else - typedef char* (*CERTGetNameFunc)(CERTName* name); -#endif - - // TODO(jcampan): add business_category and serial_number. - // TODO(wtc): NSS has the CERT_GetOrgName, CERT_GetOrgUnitName, and - // CERT_GetDomainComponentName functions, but they return only the most - // general (the first) RDN. NSS doesn't have a function for the street - // address. - static const SECOidTag kOIDs[] = { - SEC_OID_AVA_STREET_ADDRESS, - SEC_OID_AVA_ORGANIZATION_NAME, - SEC_OID_AVA_ORGANIZATIONAL_UNIT_NAME, - SEC_OID_AVA_DC }; - - std::vector<std::string>* values[] = { - &principal->street_addresses, - &principal->organization_names, - &principal->organization_unit_names, - &principal->domain_components }; - DCHECK_EQ(arraysize(kOIDs), arraysize(values)); - - CERTRDN** rdns = name->rdns; - for (size_t rdn = 0; rdns[rdn]; ++rdn) { - CERTAVA** avas = rdns[rdn]->avas; - for (size_t pair = 0; avas[pair] != 0; ++pair) { - SECOidTag tag = CERT_GetAVATag(avas[pair]); - for (size_t oid = 0; oid < arraysize(kOIDs); ++oid) { - if (kOIDs[oid] == tag) { - SECItem* decode_item = CERT_DecodeAVAValue(&avas[pair]->value); - if (!decode_item) - break; - // TODO(wtc): Pass decode_item to CERT_RFC1485_EscapeAndQuote. - std::string value(reinterpret_cast<char*>(decode_item->data), - decode_item->len); - values[oid]->push_back(value); - SECITEM_FreeItem(decode_item, PR_TRUE); - break; - } - } - } - } - - // Get CN, L, S, and C. - CERTGetNameFunc get_name_funcs[4] = { - CERT_GetCommonName, CERT_GetLocalityName, - CERT_GetStateName, CERT_GetCountryName }; - std::string* single_values[4] = { - &principal->common_name, &principal->locality_name, - &principal->state_or_province_name, &principal->country_name }; - for (size_t i = 0; i < arraysize(get_name_funcs); ++i) { - char* value = get_name_funcs[i](name); - if (value) { - single_values[i]->assign(value); - PORT_Free(value); - } - } -} - -void ParseDate(const SECItem* der_date, base::Time* result) { - PRTime prtime; - SECStatus rv = DER_DecodeTimeChoice(&prtime, der_date); - DCHECK_EQ(SECSuccess, rv); - *result = crypto::PRTimeToBaseTime(prtime); -} - -std::string ParseSerialNumber(const CERTCertificate* certificate) { - return std::string(reinterpret_cast<char*>(certificate->serialNumber.data), - certificate->serialNumber.len); -} - -void GetSubjectAltName(CERTCertificate* cert_handle, - std::vector<std::string>* dns_names, - std::vector<std::string>* ip_addrs) { - if (dns_names) - dns_names->clear(); - if (ip_addrs) - ip_addrs->clear(); - - SECItem alt_name; - SECStatus rv = CERT_FindCertExtension(cert_handle, - SEC_OID_X509_SUBJECT_ALT_NAME, - &alt_name); - if (rv != SECSuccess) - return; - - PLArenaPool* arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); - DCHECK(arena != NULL); - - CERTGeneralName* alt_name_list; - alt_name_list = CERT_DecodeAltNameExtension(arena, &alt_name); - SECITEM_FreeItem(&alt_name, PR_FALSE); - - CERTGeneralName* name = alt_name_list; - while (name) { - // DNSName and IPAddress are encoded as IA5String and OCTET STRINGs - // respectively, both of which can be byte copied from - // SECItemType::data into the appropriate output vector. - if (dns_names && name->type == certDNSName) { - dns_names->push_back(std::string( - reinterpret_cast<char*>(name->name.other.data), - name->name.other.len)); - } else if (ip_addrs && name->type == certIPAddress) { - ip_addrs->push_back(std::string( - reinterpret_cast<char*>(name->name.other.data), - name->name.other.len)); - } - name = CERT_GetNextGeneralName(name); - if (name == alt_name_list) - break; - } - PORT_FreeArena(arena, PR_FALSE); -} - -X509Certificate::OSCertHandles CreateOSCertHandlesFromBytes( - const char* data, - int length, - X509Certificate::Format format) { - X509Certificate::OSCertHandles results; - if (length < 0) - return results; - - crypto::EnsureNSSInit(); - - if (!NSS_IsInitialized()) - return results; - - switch (format) { - case X509Certificate::FORMAT_SINGLE_CERTIFICATE: { - X509Certificate::OSCertHandle handle = - X509Certificate::CreateOSCertHandleFromBytes(data, length); - if (handle) - results.push_back(handle); - break; - } - case X509Certificate::FORMAT_PKCS7: { - // Make a copy since CERT_DecodeCertPackage may modify it - std::vector<char> data_copy(data, data + length); - - SECStatus result = CERT_DecodeCertPackage(&data_copy[0], - length, CollectCertsCallback, &results); - if (result != SECSuccess) - results.clear(); - break; - } - default: - NOTREACHED() << "Certificate format " << format << " unimplemented"; - break; - } - - return results; -} - -X509Certificate::OSCertHandle ReadOSCertHandleFromPickle( - PickleIterator* pickle_iter) { - const char* data; - int length; - if (!pickle_iter->ReadData(&data, &length)) - return NULL; - - return X509Certificate::CreateOSCertHandleFromBytes(data, length); -} - -void GetPublicKeyInfo(CERTCertificate* handle, - size_t* size_bits, - X509Certificate::PublicKeyType* type) { - // Since we might fail, set the output parameters to default values first. - *type = X509Certificate::kPublicKeyTypeUnknown; - *size_bits = 0; - - crypto::ScopedSECKEYPublicKey key(CERT_ExtractPublicKey(handle)); - if (!key.get()) - return; - - *size_bits = SECKEY_PublicKeyStrengthInBits(key.get()); - - switch (key->keyType) { - case rsaKey: - *type = X509Certificate::kPublicKeyTypeRSA; - break; - case dsaKey: - *type = X509Certificate::kPublicKeyTypeDSA; - break; - case dhKey: - *type = X509Certificate::kPublicKeyTypeDH; - break; - case ecKey: - *type = X509Certificate::kPublicKeyTypeECDSA; - break; - default: - *type = X509Certificate::kPublicKeyTypeUnknown; - *size_bits = 0; - break; - } -} - -bool GetIssuersFromEncodedList( - const std::vector<std::string>& encoded_issuers, - PLArenaPool* arena, - std::vector<CERTName*>* out) { - std::vector<CERTName*> result; - for (size_t n = 0; n < encoded_issuers.size(); ++n) { - CERTName* name = CreateCertNameFromEncoded(arena, encoded_issuers[n]); - if (name != NULL) - result.push_back(name); - } - - if (result.size() == encoded_issuers.size()) { - out->swap(result); - return true; - } - - for (size_t n = 0; n < result.size(); ++n) - CERT_DestroyName(result[n]); - return false; -} - - -bool IsCertificateIssuedBy(const std::vector<CERTCertificate*>& cert_chain, - const std::vector<CERTName*>& valid_issuers) { - for (size_t n = 0; n < cert_chain.size(); ++n) { - CERTName* cert_issuer = &cert_chain[n]->issuer; - for (size_t i = 0; i < valid_issuers.size(); ++i) { - if (CERT_CompareName(valid_issuers[i], cert_issuer) == SECEqual) - return true; - } - } - return false; -} - -std::string GetUniqueNicknameForSlot(const std::string& nickname, - const SECItem* subject, - PK11SlotInfo* slot) { - int index = 2; - std::string new_name = nickname; - std::string temp_nickname = new_name; - std::string token_name; - - if (!slot) - return new_name; - - if (!PK11_IsInternalKeySlot(slot)) { - token_name.assign(PK11_GetTokenName(slot)); - token_name.append(":"); - - temp_nickname = token_name + new_name; - } - - while (SEC_CertNicknameConflict(temp_nickname.c_str(), - const_cast<SECItem*>(subject), - CERT_GetDefaultCertDB())) { - base::SStringPrintf(&new_name, "%s #%d", nickname.c_str(), index++); - temp_nickname = token_name + new_name; - } - - return new_name; -} - -#endif // defined(USE_NSS_CERTS) || defined(OS_IOS) - } // namespace x509_util } // namespace net
diff --git a/net/cert/x509_util_nss_certs.cc b/net/cert/x509_util_nss_certs.cc new file mode 100644 index 0000000..b308355 --- /dev/null +++ b/net/cert/x509_util_nss_certs.cc
@@ -0,0 +1,346 @@ +// 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 <cert.h> // Must be included before certdb.h +#include <certdb.h> +#include <cryptohi.h> +#include <nss.h> +#include <pk11pub.h> +#include <prerror.h> +#include <secder.h> +#include <secmod.h> +#include <secport.h> + +#include "base/debug/leak_annotations.h" +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/singleton.h" +#include "base/pickle.h" +#include "base/strings/stringprintf.h" +#include "crypto/ec_private_key.h" +#include "crypto/nss_util.h" +#include "crypto/nss_util_internal.h" +#include "crypto/rsa_private_key.h" +#include "crypto/scoped_nss_types.h" +#include "crypto/third_party/nss/chromium-nss.h" +#include "net/cert/x509_certificate.h" +#include "net/cert/x509_util.h" +#include "net/cert/x509_util_nss.h" + +namespace net { + +namespace { + +// Callback for CERT_DecodeCertPackage(), used in +// CreateOSCertHandlesFromBytes(). +SECStatus PR_CALLBACK +CollectCertsCallback(void* arg, SECItem** certs, int num_certs) { + X509Certificate::OSCertHandles* results = + reinterpret_cast<X509Certificate::OSCertHandles*>(arg); + + for (int i = 0; i < num_certs; ++i) { + X509Certificate::OSCertHandle handle = + X509Certificate::CreateOSCertHandleFromBytes( + reinterpret_cast<char*>(certs[i]->data), certs[i]->len); + if (handle) + results->push_back(handle); + } + + return SECSuccess; +} + +typedef scoped_ptr<CERTName, crypto::NSSDestroyer<CERTName, CERT_DestroyName>> + ScopedCERTName; + +// Create a new CERTName object from its encoded representation. +// |arena| is the allocation pool to use. +// |data| points to a DER-encoded X.509 DistinguishedName. +// Return a new CERTName pointer on success, or NULL. +CERTName* CreateCertNameFromEncoded(PLArenaPool* arena, + const base::StringPiece& data) { + if (!arena) + return NULL; + + ScopedCERTName name(PORT_ArenaZNew(arena, CERTName)); + if (!name.get()) + return NULL; + + SECItem item; + item.len = static_cast<unsigned int>(data.length()); + item.data = reinterpret_cast<unsigned char*>(const_cast<char*>(data.data())); + + SECStatus rv = SEC_ASN1DecodeItem(arena, name.get(), + SEC_ASN1_GET(CERT_NameTemplate), &item); + if (rv != SECSuccess) + return NULL; + + return name.release(); +} + +} // namespace + +namespace x509_util { + +void ParsePrincipal(CERTName* name, CertPrincipal* principal) { +// Starting in NSS 3.15, CERTGetNameFunc takes a const CERTName* argument. +#if NSS_VMINOR >= 15 + typedef char* (*CERTGetNameFunc)(const CERTName* name); +#else + typedef char* (*CERTGetNameFunc)(CERTName* name); +#endif + + // TODO(jcampan): add business_category and serial_number. + // TODO(wtc): NSS has the CERT_GetOrgName, CERT_GetOrgUnitName, and + // CERT_GetDomainComponentName functions, but they return only the most + // general (the first) RDN. NSS doesn't have a function for the street + // address. + static const SECOidTag kOIDs[] = {SEC_OID_AVA_STREET_ADDRESS, + SEC_OID_AVA_ORGANIZATION_NAME, + SEC_OID_AVA_ORGANIZATIONAL_UNIT_NAME, + SEC_OID_AVA_DC}; + + std::vector<std::string>* values[] = {&principal->street_addresses, + &principal->organization_names, + &principal->organization_unit_names, + &principal->domain_components}; + DCHECK_EQ(arraysize(kOIDs), arraysize(values)); + + CERTRDN** rdns = name->rdns; + for (size_t rdn = 0; rdns[rdn]; ++rdn) { + CERTAVA** avas = rdns[rdn]->avas; + for (size_t pair = 0; avas[pair] != 0; ++pair) { + SECOidTag tag = CERT_GetAVATag(avas[pair]); + for (size_t oid = 0; oid < arraysize(kOIDs); ++oid) { + if (kOIDs[oid] == tag) { + SECItem* decode_item = CERT_DecodeAVAValue(&avas[pair]->value); + if (!decode_item) + break; + // TODO(wtc): Pass decode_item to CERT_RFC1485_EscapeAndQuote. + std::string value(reinterpret_cast<char*>(decode_item->data), + decode_item->len); + values[oid]->push_back(value); + SECITEM_FreeItem(decode_item, PR_TRUE); + break; + } + } + } + } + + // Get CN, L, S, and C. + CERTGetNameFunc get_name_funcs[4] = {CERT_GetCommonName, + CERT_GetLocalityName, + CERT_GetStateName, + CERT_GetCountryName}; + std::string* single_values[4] = {&principal->common_name, + &principal->locality_name, + &principal->state_or_province_name, + &principal->country_name}; + for (size_t i = 0; i < arraysize(get_name_funcs); ++i) { + char* value = get_name_funcs[i](name); + if (value) { + single_values[i]->assign(value); + PORT_Free(value); + } + } +} + +void ParseDate(const SECItem* der_date, base::Time* result) { + PRTime prtime; + SECStatus rv = DER_DecodeTimeChoice(&prtime, der_date); + DCHECK_EQ(SECSuccess, rv); + *result = crypto::PRTimeToBaseTime(prtime); +} + +std::string ParseSerialNumber(const CERTCertificate* certificate) { + return std::string(reinterpret_cast<char*>(certificate->serialNumber.data), + certificate->serialNumber.len); +} + +void GetSubjectAltName(CERTCertificate* cert_handle, + std::vector<std::string>* dns_names, + std::vector<std::string>* ip_addrs) { + if (dns_names) + dns_names->clear(); + if (ip_addrs) + ip_addrs->clear(); + + SECItem alt_name; + SECStatus rv = CERT_FindCertExtension( + cert_handle, SEC_OID_X509_SUBJECT_ALT_NAME, &alt_name); + if (rv != SECSuccess) + return; + + PLArenaPool* arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + DCHECK(arena != NULL); + + CERTGeneralName* alt_name_list; + alt_name_list = CERT_DecodeAltNameExtension(arena, &alt_name); + SECITEM_FreeItem(&alt_name, PR_FALSE); + + CERTGeneralName* name = alt_name_list; + while (name) { + // DNSName and IPAddress are encoded as IA5String and OCTET STRINGs + // respectively, both of which can be byte copied from + // SECItemType::data into the appropriate output vector. + if (dns_names && name->type == certDNSName) { + dns_names->push_back( + std::string(reinterpret_cast<char*>(name->name.other.data), + name->name.other.len)); + } else if (ip_addrs && name->type == certIPAddress) { + ip_addrs->push_back( + std::string(reinterpret_cast<char*>(name->name.other.data), + name->name.other.len)); + } + name = CERT_GetNextGeneralName(name); + if (name == alt_name_list) + break; + } + PORT_FreeArena(arena, PR_FALSE); +} + +X509Certificate::OSCertHandles CreateOSCertHandlesFromBytes( + const char* data, + int length, + X509Certificate::Format format) { + X509Certificate::OSCertHandles results; + if (length < 0) + return results; + + crypto::EnsureNSSInit(); + + if (!NSS_IsInitialized()) + return results; + + switch (format) { + case X509Certificate::FORMAT_SINGLE_CERTIFICATE: { + X509Certificate::OSCertHandle handle = + X509Certificate::CreateOSCertHandleFromBytes(data, length); + if (handle) + results.push_back(handle); + break; + } + case X509Certificate::FORMAT_PKCS7: { + // Make a copy since CERT_DecodeCertPackage may modify it + std::vector<char> data_copy(data, data + length); + + SECStatus result = CERT_DecodeCertPackage(&data_copy[0], length, + CollectCertsCallback, &results); + if (result != SECSuccess) + results.clear(); + break; + } + default: + NOTREACHED() << "Certificate format " << format << " unimplemented"; + break; + } + + return results; +} + +X509Certificate::OSCertHandle ReadOSCertHandleFromPickle( + PickleIterator* pickle_iter) { + const char* data; + int length; + if (!pickle_iter->ReadData(&data, &length)) + return NULL; + + return X509Certificate::CreateOSCertHandleFromBytes(data, length); +} + +void GetPublicKeyInfo(CERTCertificate* handle, + size_t* size_bits, + X509Certificate::PublicKeyType* type) { + // Since we might fail, set the output parameters to default values first. + *type = X509Certificate::kPublicKeyTypeUnknown; + *size_bits = 0; + + crypto::ScopedSECKEYPublicKey key(CERT_ExtractPublicKey(handle)); + if (!key.get()) + return; + + *size_bits = SECKEY_PublicKeyStrengthInBits(key.get()); + + switch (key->keyType) { + case rsaKey: + *type = X509Certificate::kPublicKeyTypeRSA; + break; + case dsaKey: + *type = X509Certificate::kPublicKeyTypeDSA; + break; + case dhKey: + *type = X509Certificate::kPublicKeyTypeDH; + break; + case ecKey: + *type = X509Certificate::kPublicKeyTypeECDSA; + break; + default: + *type = X509Certificate::kPublicKeyTypeUnknown; + *size_bits = 0; + break; + } +} + +bool GetIssuersFromEncodedList(const std::vector<std::string>& encoded_issuers, + PLArenaPool* arena, + std::vector<CERTName*>* out) { + std::vector<CERTName*> result; + for (size_t n = 0; n < encoded_issuers.size(); ++n) { + CERTName* name = CreateCertNameFromEncoded(arena, encoded_issuers[n]); + if (name != NULL) + result.push_back(name); + } + + if (result.size() == encoded_issuers.size()) { + out->swap(result); + return true; + } + + for (size_t n = 0; n < result.size(); ++n) + CERT_DestroyName(result[n]); + return false; +} + +bool IsCertificateIssuedBy(const std::vector<CERTCertificate*>& cert_chain, + const std::vector<CERTName*>& valid_issuers) { + for (size_t n = 0; n < cert_chain.size(); ++n) { + CERTName* cert_issuer = &cert_chain[n]->issuer; + for (size_t i = 0; i < valid_issuers.size(); ++i) { + if (CERT_CompareName(valid_issuers[i], cert_issuer) == SECEqual) + return true; + } + } + return false; +} + +std::string GetUniqueNicknameForSlot(const std::string& nickname, + const SECItem* subject, + PK11SlotInfo* slot) { + int index = 2; + std::string new_name = nickname; + std::string temp_nickname = new_name; + std::string token_name; + + if (!slot) + return new_name; + + if (!PK11_IsInternalKeySlot(slot)) { + token_name.assign(PK11_GetTokenName(slot)); + token_name.append(":"); + + temp_nickname = token_name + new_name; + } + + while (SEC_CertNicknameConflict(temp_nickname.c_str(), + const_cast<SECItem*>(subject), + CERT_GetDefaultCertDB())) { + base::SStringPrintf(&new_name, "%s #%d", nickname.c_str(), index++); + temp_nickname = token_name + new_name; + } + + return new_name; +} + +} // namespace x509_util + +} // namespace net
diff --git a/net/cert_net/cert_net_fetcher_impl.cc b/net/cert_net/cert_net_fetcher_impl.cc index 7b119ca..51947e32 100644 --- a/net/cert_net/cert_net_fetcher_impl.cc +++ b/net/cert_net/cert_net_fetcher_impl.cc
@@ -272,7 +272,7 @@ // If there are no longer any requests attached to the job then // cancel and delete it. - if (requests_.empty()) + if (requests_.empty() && !parent_->IsCurrentlyCompletingJob(this)) delete_this = parent_->RemoveJob(this); } @@ -532,7 +532,7 @@ scoped_ptr<CertNetFetcherImpl::Job> CertNetFetcherImpl::RemoveJob(Job* job) { DCHECK(thread_checker_.CalledOnValidThread()); bool erased_job = jobs_.erase(job) == 1; - DCHECK(erased_job); + CHECK(erased_job); return make_scoped_ptr(job); } @@ -547,4 +547,8 @@ currently_completing_job_ = nullptr; } +bool CertNetFetcherImpl::IsCurrentlyCompletingJob(Job* job) { + return job == currently_completing_job_; +} + } // namespace net
diff --git a/net/cert_net/cert_net_fetcher_impl.h b/net/cert_net/cert_net_fetcher_impl.h index 692647a..ac5cd90 100644 --- a/net/cert_net/cert_net_fetcher_impl.h +++ b/net/cert_net/cert_net_fetcher_impl.h
@@ -84,6 +84,7 @@ // Indicates which Job is currently executing inside of OnJobCompleted(). void SetCurrentlyCompletingJob(Job* job); void ClearCurrentlyCompletingJob(Job* job); + bool IsCurrentlyCompletingJob(Job* job); // The in-progress jobs. This set does not contain the job which is actively // invoking callbacks (OnJobCompleted). Instead that is tracked by
diff --git a/net/cert_net/cert_net_fetcher_impl_unittest.cc b/net/cert_net/cert_net_fetcher_impl_unittest.cc index 2003a41..c803b4c 100644 --- a/net/cert_net/cert_net_fetcher_impl_unittest.cc +++ b/net/cert_net/cert_net_fetcher_impl_unittest.cc
@@ -726,4 +726,33 @@ EXPECT_FALSE(callback[2].HasResult()); } +// Cancel the final request while executing a callback for the same job. Ensure +// that the job is not deleted twice. +TEST_F(CertNetFetcherImplTest, CancelLastRequestWithinCallback) { + ASSERT_TRUE(test_server_.Start()); + + CertNetFetcherImpl fetcher(&context_); + + GURL url = test_server_.GetURL("files/cert.crt"); + + TestFetchCallback callback1; + scoped_ptr<CertNetFetcher::Request> request1 = + StartRequest(&fetcher, url, callback1); + + TestFetchCallback callback2; + scoped_ptr<CertNetFetcher::Request> request2 = + StartRequest(&fetcher, url, callback1); + + // Cancel request2 when the callback for request1 runs. + callback1.set_extra_closure(base::Bind(CancelRequest, &request2)); + + EXPECT_EQ(1, network_delegate_.created_requests()); + + scoped_ptr<FetchResult> result = callback1.WaitForResult(); + result->VerifySuccess("-cert.crt-\n"); + + // request2 was cancelled. + EXPECT_FALSE(callback2.HasResult()); +} + } // namespace net
diff --git a/net/cookies/cookie_monster.cc b/net/cookies/cookie_monster.cc index d799758..d335ae4 100644 --- a/net/cookies/cookie_monster.cc +++ b/net/cookies/cookie_monster.cc
@@ -557,7 +557,7 @@ : CookieMonsterTask(cookie_monster), callback_(callback) {} // CookieMonsterTask: - virtual void Run() override; + void Run() override; protected: ~DeleteTask() override;
diff --git a/net/der/input.cc b/net/der/input.cc new file mode 100644 index 0000000..7c875a4 --- /dev/null +++ b/net/der/input.cc
@@ -0,0 +1,90 @@ +// 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/logging.h" +#include "net/der/input.h" + +namespace net { + +namespace der { + +Input::Input() : data_(nullptr), len_(0) { +} +Input::Input(const uint8_t* data, size_t len) : data_(data), len_(len) { +} +Input::Input(const std::string& s) + : data_(reinterpret_cast<const uint8_t*>(s.data())), len_(s.size()) { +} + +ByteReader::ByteReader(const Input& in) + : data_(in.UnsafeData()), len_(in.Length()) { +} + +bool ByteReader::ReadByte(uint8_t* byte_p) { + if (!HasMore()) + return false; + *byte_p = *data_; + Advance(1); + return true; +} + +bool ByteReader::ReadBytes(size_t len, Input* out) { + if (len > len_) + return false; + *out = Input(data_, len); + Advance(len); + return true; +} + +// Returns whether there is any more data to be read. +bool ByteReader::HasMore() { + return len_ > 0; +} + +Mark ByteReader::NewMark() { + return Mark(data_); +} + +bool ByteReader::AdvanceToMark(Mark mark) { + if (mark.ptr_ < data_) + return false; + // mark.ptr_ >= data_, so no concern of integer underflow here. + size_t advance_len = mark.ptr_ - data_; + if (advance_len > len_) + return false; + Advance(advance_len); + return true; +} + +bool ByteReader::ReadToMark(Mark mark, Input* out) { + if (mark.ptr_ < data_) + return false; + // mark.ptr_ >= data_, so no concern of integer underflow here. + size_t len = mark.ptr_ - data_; + return ReadBytes(len, out); +} + +void ByteReader::Advance(size_t len) { + CHECK_LE(len, len_); + data_ += len; + len_ -= len; +} + +Mark Mark::NullMark() { + return Mark(); +} + +bool Mark::IsEmpty() { + return ptr_ == nullptr; +} + +Mark::Mark(const uint8_t* ptr) : ptr_(ptr) { +} + +Mark::Mark() : ptr_(nullptr) { +} + +} // namespace der + +} // namespace net
diff --git a/net/der/input.h b/net/der/input.h new file mode 100644 index 0000000..667bd89 --- /dev/null +++ b/net/der/input.h
@@ -0,0 +1,152 @@ +// 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 NET_DER_INPUT_H_ +#define NET_DER_INPUT_H_ + +#include <stdint.h> +#include <string> + +#include "base/compiler_specific.h" +#include "net/base/net_export.h" + +namespace net { + +namespace der { + +class Mark; + +// An opaque class that represents a fixed buffer of data of a fixed length, +// to be used as an input to other operations. An Input object does not own +// the data it references, so callers are responsible for making sure that +// the data outlives the Input object and any other associated objects. +// +// All data access for an Input should be done through the ByteReader and Mark +// classes. This class and associated classes are designed with safety in mind +// to make it difficult to read memory outside of an Input. ByteReader provides +// a simple API for reading through the Input sequentially. For more +// complicated uses, multiple instances of a ByteReader for a particular Input +// can be created, and instances of Mark can be used to coordinate between the +// ByteReaders. +// +// One such use case of multiple ByteReaders would be looking ahead in an +// input: A ByteReader is copied and then is used to read some number of +// bytes into the input, based on the content it is reading. A Mark can then be +// set using the temporary ByteReader to indicate how far it read into the +// Input. The original ByteReader can then be synchronized with how far the +// temporary ByteReader read, by using either AdvanceToMark() or ReadToMark(). +class NET_EXPORT_PRIVATE Input { + public: + // Creates an empty Input, one from which no data can be read. + Input(); + + // Creates an Input from the given |data| and |len|. + Input(const uint8_t* data, size_t len); + + // Creates an Input from the given string |s|. + explicit Input(const std::string& s); + + // Returns the length in bytes of an Input's data. + size_t Length() const { return len_; } + + // Returns a pointer to the Input's data. This method is marked as "unsafe" + // because access to the Input's data should be done through ByteReader + // instead. This method should only be used where using a ByteReader truly + // is not an option. + const uint8_t* UnsafeData() const { return data_; } + + private: + const uint8_t* data_; + size_t len_; +}; + +// This class provides ways to read data from an Input in a bounds-checked way. +// The ByteReader is designed to read through the input sequentially. Once a +// byte has been read with a ByteReader, the caller can't go back and re-read +// that byte with the same reader. Of course, the caller can create multiple +// ByteReaders for the same input (or copy an existing ByteReader). +// +// For something simple like a single byte lookahead, the easiest way to do +// that is to copy the ByteReader and call ReadByte() on the copy - the original +// ByteReader will be unaffected and the peeked byte will be read through +// ReadByte(). For other read patterns, it can be useful to mark where one is +// in a ByteReader to be able to return to that spot. +// +// Some operations using Mark can also be done by creating a copy of the +// ByteReader. By using a Mark instead, you use less memory, but more +// importantly, you end up with an immutable object that matches the semantics +// of what is intended. +class NET_EXPORT_PRIVATE ByteReader { + public: + // Creates a ByteReader to read the data represented by an Input. + explicit ByteReader(const Input& in); + + // Reads a single byte from the input source, putting the byte read in + // |*byte_p|. If a byte cannot be read from the input (because there is + // no input left), then this method returns false. + bool ReadByte(uint8_t* out) WARN_UNUSED_RESULT; + + // Reads |len| bytes from the input source, and initializes an Input to + // point to that data. If there aren't enough bytes left in the input source, + // then this method returns false. + bool ReadBytes(size_t len, Input* out) WARN_UNUSED_RESULT; + + // Returns how many bytes are left to read. + size_t BytesLeft() const { return len_; } + + // Returns whether there is any more data to be read. + bool HasMore(); + + // Creates a new Mark at the current position of this ByteReader. If another + // ByteReader is advanced to this mark, the next byte read by the other + // ByteReader will be the same as the next byte read by this ByteReader. + Mark NewMark(); + + // Advances this ByteReader to the position marked by |mark|. Note that + // a ByteReader can only advance forward - it is not possible to "rewind" + // to a previous mark. To do this, one would need to create a new ByteReader + // (from the same input) and AdvanceToMark() on the new ByteReader. + // + // If it is not possible to advance this ByteReader to the mark, this method + // returns false and does nothing. + bool AdvanceToMark(Mark mark) WARN_UNUSED_RESULT; + + // Reads all data from the cursor of this ByteReader up to the mark, and + // initializes an Input to point to that data. If the Mark is not valid for + // this ByteReader, then this method returns false and does not modify |*out|. + bool ReadToMark(Mark mark, Input* out) WARN_UNUSED_RESULT; + + private: + void Advance(size_t len); + + const uint8_t* data_; + size_t len_; +}; + +// An immutable opaque pointer into the data represented by an Input. A Mark +// object is used to save and restore the state (position) of a ByteReader to +// allow for lookahead or backtracking. All interaction with a Mark object is +// done through the ByteReader class. +class NET_EXPORT_PRIVATE Mark { + public: + // Creates a null Mark. This Mark will not be usable with any ByteReader. + // This only exists so that a class can have a Mark member which may or may + // not be a valid Mark at any given time. + static Mark NullMark(); + + // Checks whether a given Mark is an empty Mark. + bool IsEmpty(); + friend class ByteReader; + + private: + explicit Mark(const uint8_t* ptr); + Mark(); + const uint8_t* ptr_; +}; + +} // namespace der + +} // namespace net + +#endif // NET_DER_INPUT_H_
diff --git a/net/der/input_unittest.cc b/net/der/input_unittest.cc new file mode 100644 index 0000000..a6a9b8c --- /dev/null +++ b/net/der/input_unittest.cc
@@ -0,0 +1,125 @@ +// 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/logging.h" +#include "net/der/input.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace net { +namespace der { +namespace test { + +const uint8_t kInput[] = {'t', 'e', 's', 't'}; + +TEST(ByteReaderTest, NoReadPastEnd) { + ByteReader reader(Input(nullptr, 0)); + uint8_t data; + EXPECT_FALSE(reader.ReadByte(&data)); +} + +TEST(ByteReaderTest, ReadToEnd) { + uint8_t out; + ByteReader reader(Input(kInput, arraysize(kInput))); + for (size_t i = 0; i < arraysize(kInput); ++i) { + ASSERT_TRUE(reader.ReadByte(&out)); + ASSERT_EQ(kInput[i], out); + } + EXPECT_FALSE(reader.ReadByte(&out)); +} + +TEST(ByteReaderTest, PartialReadFails) { + Input out; + ByteReader reader(Input(kInput, arraysize(kInput))); + EXPECT_FALSE(reader.ReadBytes(5, &out)); +} + +TEST(ByteReaderTest, HasMore) { + Input out; + ByteReader reader(Input(kInput, arraysize(kInput))); + + ASSERT_TRUE(reader.HasMore()); + ASSERT_TRUE(reader.ReadBytes(arraysize(kInput), &out)); + ASSERT_FALSE(reader.HasMore()); +} + +TEST(ByteReaderTest, ReadToMark) { + uint8_t out; + Input input(kInput, arraysize(kInput)); + ByteReader reader(input); + + // Read 2 bytes from the reader and then set a mark. + ASSERT_TRUE(reader.ReadByte(&out)); + ASSERT_TRUE(reader.ReadByte(&out)); + Mark mark = reader.NewMark(); + + // Reset the reader and check that we can read to a mark previously set. + reader = ByteReader(input); + Input marked_data; + ASSERT_TRUE(reader.ReadToMark(mark, &marked_data)); +} + +TEST(ByteReaderTest, CantReadToWrongMark) { + Input out; + Input in1(kInput, arraysize(kInput)); + Input in2("test"); + ByteReader reader1(in1); + ByteReader reader2(in2); + ASSERT_TRUE(reader1.ReadBytes(2, &out)); + ASSERT_TRUE(reader2.ReadBytes(2, &out)); + Mark mark1 = reader1.NewMark(); + Mark mark2 = reader2.NewMark(); + reader1 = ByteReader(in1); + reader2 = ByteReader(in2); + + // It is not possible to advance to a mark outside the underlying input. + ASSERT_FALSE(reader1.AdvanceToMark(mark2)); + ASSERT_FALSE(reader2.AdvanceToMark(mark1)); +} + +TEST(ByteReaderTest, MarksAreSharedBetweenSameInputs) { + Input out; + Input in1(kInput, arraysize(kInput)); + Input in2(kInput, 1); + ByteReader reader1(in1); + ByteReader reader2(in2); + ASSERT_TRUE(reader1.ReadBytes(2, &out)); + ASSERT_TRUE(reader2.ReadBytes(1, &out)); + Mark mark1 = reader1.NewMark(); + Mark mark2 = reader2.NewMark(); + reader1 = ByteReader(in1); + reader2 = ByteReader(in2); + + // If Marks are created on the same underlying data, they can be shared + // across ByteReaders and Inputs. However, they still must be inside the + // bounds for the ByteReader they are being used on. + + // mark1 is past the end of the input for reader2. + EXPECT_FALSE(reader2.AdvanceToMark(mark1)); + // mark2 is within the bounds of reader1. + EXPECT_TRUE(reader1.AdvanceToMark(mark2)); +} + +TEST(ByteReaderTest, CantReadToWrongMarkWithInputsOnStack) { + const uint8_t data1[] = "test"; + const uint8_t data2[] = "foo"; + Input out; + Input in1(data1, arraysize(data1)); + Input in2(data2, arraysize(data2)); + + ByteReader reader1(in1); + ByteReader reader2(in2); + ASSERT_TRUE(reader1.ReadBytes(2, &out)); + ASSERT_TRUE(reader2.ReadBytes(2, &out)); + Mark mark1 = reader1.NewMark(); + Mark mark2 = reader2.NewMark(); + reader1 = ByteReader(in1); + reader2 = ByteReader(in2); + + ASSERT_FALSE(reader1.AdvanceToMark(mark2)); + ASSERT_FALSE(reader2.AdvanceToMark(mark1)); +} + +} // namespace test +} // namespace der +} // namespace net
diff --git a/net/der/parse_values.cc b/net/der/parse_values.cc new file mode 100644 index 0000000..bbf765f --- /dev/null +++ b/net/der/parse_values.cc
@@ -0,0 +1,269 @@ +// 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/logging.h" +#include "base/numerics/safe_math.h" +#include "net/der/parse_values.h" + +namespace net { + +namespace der { + +namespace { + +bool ParseBoolInternal(const Input& in, bool* out, bool relaxed) { + // According to ITU-T X.690 section 8.2, a bool is encoded as a single octet + // where the octet of all zeroes is FALSE and a non-zero value for the octet + // is TRUE. + if (in.Length() != 1) + return false; + ByteReader data(in); + uint8_t byte; + if (!data.ReadByte(&byte)) + return false; + if (byte == 0) { + *out = false; + return true; + } + // ITU-T X.690 section 11.1 specifies that for DER, the TRUE value must be + // encoded as an octet of all ones. + if (byte == 0xff || relaxed) { + *out = true; + return true; + } + return false; +} + +// Reads a positive decimal number with |digits| digits and stores it in +// |*out|. This function does not check that the type of |*out| is large +// enough to hold 10^digits - 1; the caller must choose an appropriate type +// based on the number of digits they wish to parse. +template <typename UINT> +bool DecimalStringToUint(ByteReader& in, size_t digits, UINT* out) { + UINT value = 0; + for (size_t i = 0; i < digits; ++i) { + uint8_t digit; + if (!in.ReadByte(&digit)) { + return false; + } + if (digit < '0' || digit > '9') { + return false; + } + value = (value * 10) + (digit - '0'); + } + *out = value; + return true; +} + +// Checks that the values in a GeneralizedTime struct are valid. This involves +// checking that the year is 4 digits, the month is between 1 and 12, the day +// is a day that exists in that month (following current leap year rules), +// hours are between 0 and 23, minutes between 0 and 59, and seconds between +// 0 and 60 (to allow for leap seconds; no validation is done that a leap +// second is on a day that could be a leap second). +bool ValidateGeneralizedTime(const GeneralizedTime& time) { + CHECK(time.year > 0 && time.year < 9999); + if (time.month < 1 || time.month > 12) + return false; + if (time.day < 1) + return false; + if (time.hours < 0 || time.hours > 23) + return false; + if (time.minutes < 0 || time.minutes > 59) + return false; + // Leap seconds are allowed. + if (time.seconds < 0 || time.seconds > 60) + return false; + + // validate upper bound for day of month + switch (time.month) { + case 4: + case 6: + case 9: + case 11: + if (time.day > 30) + return false; + break; + case 1: + case 3: + case 5: + case 7: + case 8: + case 10: + case 12: + if (time.day > 31) + return false; + break; + case 2: + if (time.year % 4 == 0 && + (time.year % 100 != 0 || time.year % 400 == 0)) { + if (time.day > 29) + return false; + } else { + if (time.day > 28) + return false; + } + break; + default: + NOTREACHED(); + return false; + } + return true; +} + +} // namespace + +bool ParseBool(const Input& in, bool* out) { + return ParseBoolInternal(in, out, false /* relaxed */); +} + +// BER interprets any non-zero value as true, while DER requires a bool to +// have either all bits zero (false) or all bits one (true). To support +// malformed certs, we recognized the BER encoding instead of failing to +// parse. +bool ParseBoolRelaxed(const Input& in, bool* out) { + return ParseBoolInternal(in, out, true /* relaxed */); +} + +bool ParseUint64(const Input& in, uint64_t* out) { + ByteReader reader(in); + size_t bytes_read = 0; + uint8_t data; + uint64_t value = 0; + // Note that for simplicity, this check only admits integers up to 2^63-1. + if (in.Length() > sizeof(uint64_t) || in.Length() == 0) + return false; + while (reader.ReadByte(&data)) { + if (bytes_read == 0 && (data & 0x80)) { + return false; + } + value <<= 8; + value |= data; + bytes_read++; + } + // ITU-T X.690 section 8.3.2 specifies that an integer value must be encoded + // in the smallest number of octets. If the encoding consists of more than + // one octet, then the bits of the first octet and the most significant bit + // of the second octet must not be all zeroes or all ones. + // Because this function only parses unsigned integers, there's no need to + // check for the all ones case. + if (bytes_read > 1) { + ByteReader first_bytes_reader(in); + uint8_t first_byte; + uint8_t second_byte; + if (!first_bytes_reader.ReadByte(&first_byte) || + !first_bytes_reader.ReadByte(&second_byte)) { + return false; + } + if (first_byte == 0 && !(second_byte & 0x80)) { + return false; + } + } + *out = value; + return true; +} + +bool operator<(const GeneralizedTime& lhs, const GeneralizedTime& rhs) { + if (lhs.year != rhs.year) + return lhs.year < rhs.year; + if (lhs.month != rhs.month) + return lhs.month < rhs.month; + if (lhs.day != rhs.day) + return lhs.day < rhs.day; + if (lhs.hours != rhs.hours) + return lhs.hours < rhs.hours; + if (lhs.minutes != rhs.minutes) + return lhs.minutes < rhs.minutes; + return lhs.seconds < rhs.seconds; +} + +// A UTC Time in DER encoding should be YYMMDDHHMMSSZ, but some CAs encode +// the time following BER rules, which allows for YYMMDDHHMMZ. If the length +// is 11, assume it's YYMMDDHHMMZ, and in converting it to a GeneralizedTime, +// add in the seconds (set to 0). +bool ParseUTCTimeRelaxed(const Input& in, GeneralizedTime* value) { + ByteReader reader(in); + GeneralizedTime time; + if (!DecimalStringToUint(reader, 2, &time.year) || + !DecimalStringToUint(reader, 2, &time.month) || + !DecimalStringToUint(reader, 2, &time.day) || + !DecimalStringToUint(reader, 2, &time.hours) || + !DecimalStringToUint(reader, 2, &time.minutes)) { + return false; + } + + // Try to read the 'Z' at the end. If we read something else, then for it to + // be valid the next bytes should be seconds (and then followed by 'Z'). + uint8_t zulu; + ByteReader zulu_reader = reader; + if (!zulu_reader.ReadByte(&zulu)) + return false; + if (zulu == 'Z' && !zulu_reader.HasMore()) { + time.seconds = 0; + *value = time; + return true; + } + if (!DecimalStringToUint(reader, 2, &time.seconds)) + return false; + if (!reader.ReadByte(&zulu) || zulu != 'Z' || reader.HasMore()) + return false; + if (!ValidateGeneralizedTime(time)) + return false; + if (time.year < 50) { + time.year += 2000; + } else { + time.year += 1900; + } + *value = time; + return true; +} + +bool ParseUTCTime(const Input& in, GeneralizedTime* value) { + ByteReader reader(in); + GeneralizedTime time; + if (!DecimalStringToUint(reader, 2, &time.year) || + !DecimalStringToUint(reader, 2, &time.month) || + !DecimalStringToUint(reader, 2, &time.day) || + !DecimalStringToUint(reader, 2, &time.hours) || + !DecimalStringToUint(reader, 2, &time.minutes) || + !DecimalStringToUint(reader, 2, &time.seconds)) { + return false; + } + uint8_t zulu; + if (!reader.ReadByte(&zulu) || zulu != 'Z' || reader.HasMore()) + return false; + if (!ValidateGeneralizedTime(time)) + return false; + if (time.year < 50) { + time.year += 2000; + } else { + time.year += 1900; + } + *value = time; + return true; +} + +bool ParseGeneralizedTime(const Input& in, GeneralizedTime* value) { + ByteReader reader(in); + GeneralizedTime time; + if (!DecimalStringToUint(reader, 4, &time.year) || + !DecimalStringToUint(reader, 2, &time.month) || + !DecimalStringToUint(reader, 2, &time.day) || + !DecimalStringToUint(reader, 2, &time.hours) || + !DecimalStringToUint(reader, 2, &time.minutes) || + !DecimalStringToUint(reader, 2, &time.seconds)) { + return false; + } + uint8_t zulu; + if (!reader.ReadByte(&zulu) || zulu != 'Z' || reader.HasMore()) + return false; + if (!ValidateGeneralizedTime(time)) + return false; + *value = time; + return true; +} + +} // namespace der + +} // namespace net
diff --git a/net/der/parse_values.h b/net/der/parse_values.h new file mode 100644 index 0000000..23c534e --- /dev/null +++ b/net/der/parse_values.h
@@ -0,0 +1,69 @@ +// 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 NET_DER_PARSE_TYPES_H_ +#define NET_DER_PARSE_TYPES_H_ + +#include "base/compiler_specific.h" +#include "net/base/net_export.h" +#include "net/der/input.h" + +namespace net { + +namespace der { + +// Reads a DER-encoded ASN.1 BOOLEAN value from |in| and puts the resulting +// value in |out|. Returns whether the encoded value could successfully be +// read. +NET_EXPORT bool ParseBool(const Input& in, bool* out) WARN_UNUSED_RESULT; + +// Like ParseBool, except it is more relaxed in what inputs it accepts: Any +// value that is a valid BER encoding will be parsed successfully. +NET_EXPORT bool ParseBoolRelaxed(const Input& in, bool* out) WARN_UNUSED_RESULT; + +// Reads a DER-encoded ASN.1 INTEGER value from |in| and puts the resulting +// value in |out|. ASN.1 INTEGERs are arbitrary precision; this function is +// provided as a convenience when the caller knows that the value is unsigned +// and is between 0 and 2^63-1. This function does not support the full range of +// uint64_t. This function returns false if the value is too big to fit in a +// uint64_t, is negative, or if there is an error reading the integer. +NET_EXPORT bool ParseUint64(const Input& in, uint64_t* out) WARN_UNUSED_RESULT; + +struct GeneralizedTime { + uint16_t year; + uint8_t month; + uint8_t day; + uint8_t hours; + uint8_t minutes; + uint8_t seconds; +}; + +NET_EXPORT_PRIVATE bool operator<(const GeneralizedTime& lhs, + const GeneralizedTime& rhs); + +// Reads a DER-encoded ASN.1 UTCTime value from |in| and puts the resulting +// value in |out|, returning true if the UTCTime could be parsed successfully. +NET_EXPORT bool ParseUTCTime(const Input& in, + GeneralizedTime* out) WARN_UNUSED_RESULT; + +// Like ParseUTCTime, but it is more lenient in what is accepted. DER requires +// a UTCTime to be in the format YYMMDDhhmmssZ; this function will accept both +// that and YYMMDDhhmmZ, which is a valid BER encoding of a UTCTime which +// sometimes incorrectly appears in X.509 certificates. +NET_EXPORT bool ParseUTCTimeRelaxed(const Input& in, + GeneralizedTime* out) WARN_UNUSED_RESULT; + +// Reads a DER-encoded ASN.1 GeneralizedTime value from |in| and puts the +// resulting value in |out|, returning true if the GeneralizedTime could +// be parsed sucessfully. This function is even more restrictive than the +// DER rules - it follows the rules from RFC5280, which does not allow for +// fractional seconds. +NET_EXPORT bool ParseGeneralizedTime(const Input& in, + GeneralizedTime* out) WARN_UNUSED_RESULT; + +} // namespace der + +} // namespace net + +#endif // NET_DER_PARSE_TYPES_H_
diff --git a/net/der/parse_values_unittest.cc b/net/der/parse_values_unittest.cc new file mode 100644 index 0000000..82330424 --- /dev/null +++ b/net/der/parse_values_unittest.cc
@@ -0,0 +1,201 @@ +// 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/macros.h" +#include "net/der/parse_values.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace net { +namespace der { +namespace test { + +TEST(ParseValuesTest, ParseBool) { + uint8_t buf[] = {0xFF, 0x00}; + Input value(buf, 1); + bool out; + EXPECT_TRUE(ParseBool(value, &out)); + EXPECT_TRUE(out); + + buf[0] = 0; + EXPECT_TRUE(ParseBool(value, &out)); + EXPECT_FALSE(out); + + buf[0] = 1; + EXPECT_FALSE(ParseBool(value, &out)); + EXPECT_TRUE(ParseBoolRelaxed(value, &out)); + EXPECT_TRUE(out); + + buf[0] = 0xFF; + value = Input(buf, 2); + EXPECT_FALSE(ParseBool(value, &out)); + value = Input(buf, 0); + EXPECT_FALSE(ParseBool(value, &out)); +} + +TEST(ParseValuesTest, ParseTimes) { + GeneralizedTime out; + + EXPECT_TRUE(ParseUTCTime(Input("140218161200Z"), &out)); + + // DER-encoded UTCTime must end with 'Z'. + EXPECT_FALSE(ParseUTCTime(Input("140218161200X"), &out)); + + // Check that a negative number (-4 in this case) doesn't get parsed as + // a 2-digit number. + EXPECT_FALSE(ParseUTCTime(Input("-40218161200Z"), &out)); + + // Check that numbers with a leading 0 don't get parsed in octal by making + // the second digit an invalid octal digit (e.g. 09). + EXPECT_TRUE(ParseUTCTime(Input("090218161200Z"), &out)); + + // Check that the length is validated. + EXPECT_FALSE(ParseUTCTime(Input("140218161200"), &out)); + EXPECT_FALSE(ParseUTCTime(Input("140218161200Z0"), &out)); + EXPECT_FALSE(ParseUTCTimeRelaxed(Input("140218161200"), &out)); + EXPECT_FALSE(ParseUTCTimeRelaxed(Input("140218161200Z0"), &out)); + + // Check strictness of UTCTime parsers. + EXPECT_FALSE(ParseUTCTime(Input("1402181612Z"), &out)); + EXPECT_TRUE(ParseUTCTimeRelaxed(Input("1402181612Z"), &out)); + + // Check that the time ends in Z. + EXPECT_FALSE(ParseUTCTimeRelaxed(Input("1402181612Z0"), &out)); + + // Check format of GeneralizedTime. + + // Leap seconds are allowed. + EXPECT_TRUE(ParseGeneralizedTime(Input("20140218161260Z"), &out)); + + // But nothing larger than a leap second. + EXPECT_FALSE(ParseGeneralizedTime(Input("20140218161261Z"), &out)); + + // Minutes only go up to 59. + EXPECT_FALSE(ParseGeneralizedTime(Input("20140218166000Z"), &out)); + + // Hours only go up to 23. + EXPECT_FALSE(ParseGeneralizedTime(Input("20140218240000Z"), &out)); + // The 0th day of a month is invalid. + EXPECT_FALSE(ParseGeneralizedTime(Input("20140200161200Z"), &out)); + // The 0th month is invalid. + EXPECT_FALSE(ParseGeneralizedTime(Input("20140018161200Z"), &out)); + // Months greater than 12 are invalid. + EXPECT_FALSE(ParseGeneralizedTime(Input("20141318161200Z"), &out)); + + // Some months have 31 days. + EXPECT_TRUE(ParseGeneralizedTime(Input("20140131000000Z"), &out)); + + // September has only 30 days. + EXPECT_FALSE(ParseGeneralizedTime(Input("20140931000000Z"), &out)); + + // February has only 28 days... + EXPECT_FALSE(ParseGeneralizedTime(Input("20140229000000Z"), &out)); + + // ... unless it's a leap year. + EXPECT_TRUE(ParseGeneralizedTime(Input("20160229000000Z"), &out)); + + // There aren't any leap days in years divisible by 100... + EXPECT_FALSE(ParseGeneralizedTime(Input("21000229000000Z"), &out)); + + // ...unless it's also divisible by 400. + EXPECT_TRUE(ParseGeneralizedTime(Input("20000229000000Z"), &out)); + + // Check more perverse invalid inputs. + + const uint8_t trailing_null_bytes[] = {'2', + '0', + '0', + '0', + '1', + '2', + '3', + '1', + '0', + '1', + '0', + '2', + '0', + '3', + 'Z', + '\0'}; + Input trailing_null(trailing_null_bytes, sizeof(trailing_null_bytes)); + EXPECT_FALSE(ParseGeneralizedTime(trailing_null, &out)); + const uint8_t embedded_null_bytes[] = {'2', + '0', + '0', + '\0', + '1', + '2', + '3', + '1', + '0', + '1', + '0', + '2', + '0', + '3', + 'Z'}; + Input embedded_null(embedded_null_bytes, sizeof(embedded_null_bytes)); + EXPECT_FALSE(ParseGeneralizedTime(embedded_null, &out)); + + // The year can't be in hex. + EXPECT_FALSE(ParseGeneralizedTime(Input("0x201231000000Z"), &out)); + + // The last byte must be 'Z'. + EXPECT_FALSE(ParseGeneralizedTime(Input("20001231000000X"), &out)); + + // Check that the length is validated. + EXPECT_FALSE(ParseGeneralizedTime(Input("20140218161200"), &out)); + EXPECT_FALSE(ParseGeneralizedTime(Input("20140218161200Z0"), &out)); +} + +TEST(ParseValuesTest, TimesCompare) { + GeneralizedTime time1; + GeneralizedTime time2; + GeneralizedTime time3; + + ASSERT_TRUE(ParseGeneralizedTime(Input("20140218161200Z"), &time1)); + ASSERT_TRUE(ParseUTCTime(Input("150218161200Z"), &time2)); + ASSERT_TRUE(ParseGeneralizedTime(Input("20160218161200Z"), &time3)); + EXPECT_TRUE(time1 < time2); + EXPECT_TRUE(time2 < time3); + EXPECT_TRUE(time1 < time3); +} + +struct Uint64TestData { + bool should_pass; + const uint8_t input[9]; + size_t length; + uint64_t expected_value; +}; + +const Uint64TestData kUint64TestData[] = { + {true, {0x00}, 1, 0}, + {true, {0x01}, 1, 1}, + {false, {0xFF}, 1, 0}, + {true, {0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, 8, INT64_MAX}, + {false, {0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, 8, 0}, + {false, {0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, 9, 0}, + {false, {0x00, 0x01}, 2, 1}, + {false, {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09}, 9, 0}, + {false, {0}, 0, 0}, +}; + +TEST(ParseValuesTest, ParseUint64) { + for (size_t i = 0; i < arraysize(kUint64TestData); i++) { + Uint64TestData test_case = kUint64TestData[i]; + SCOPED_TRACE(i); + + uint64_t result; + EXPECT_EQ(test_case.should_pass, + ParseUint64(Input(test_case.input, test_case.length), &result)); + if (test_case.should_pass) + EXPECT_EQ(test_case.expected_value, result); + } +} + +} // namespace test +} // namespace der +} // namespace net
diff --git a/net/der/parser.cc b/net/der/parser.cc new file mode 100644 index 0000000..7263fba --- /dev/null +++ b/net/der/parser.cc
@@ -0,0 +1,192 @@ +// 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/logging.h" +#include "base/numerics/safe_math.h" +#include "net/der/parse_values.h" +#include "net/der/parser.h" + +namespace net { + +namespace der { + +Parser::Parser() : input_(Input()), advance_mark_(Mark::NullMark()) { +} +Parser::Parser(const Input& input) + : input_(input), advance_mark_(Mark::NullMark()) { +} + +// Reads the next TLV from the input and writes the tag and value to the +// output parameters |tag| and |out|. +bool Parser::PeekTagAndValue(Tag* tag, Input* out) { + ByteReader reader = input_; + + // Don't support tags > 30. + uint8_t tag_byte; + if (!reader.ReadByte(&tag_byte)) + return false; + + // ITU-T X.690 section 8.1.2.3 specifies the format for identifiers with a + // tag number no greater than 30. This parser only supports tag numbers up + // to 30. + // If the tag number is 31 (0x1F, the largest value that fits in the allotted + // bytes), then the tag is more than one byte long and the continuation bytes + // contain the real tag number. We only support tag numbers < 31 (and thus + // single-byte tags). + if ((tag_byte & kTagNumberMask) == 31) + return false; + + // Parse length. The format for the length encoding is specified in + // ITU-T X.690 section 8.1.3. + size_t value_len = 0; // Number of bytes used to encode just the value. + + uint8_t length_first_byte; + if (!reader.ReadByte(&length_first_byte)) + return false; + if ((length_first_byte & 0x80) == 0) { + // Short form for length - it's only one byte long. + value_len = length_first_byte & 0x7f; + } else { + // Long form for length - it's encoded across multiple bytes. + if (length_first_byte == 0xff) { + // ITU-T X.690 clause 8.1.3.5.c specifies the value 0xff shall not be + // used. + return false; + } + // The high bit indicated that this is the long form, while the next 7 bits + // encode the number of subsequent octets used to encode the length + // (ITU-T X.690 clause 8.1.3.5.b). + size_t length_len = length_first_byte & 0x7f; + if (length_len == 0) { + // ITU-T X.690 section 10.1 (DER length forms) requires encoding the + // length with the minimum number of octets. Besides, it makes no sense + // for the length to be encoded in 0 octets. + return false; + } + if (length_len > sizeof(value_len)) { + // The length is encoded in multiple octets, with the first octet + // indicating how many octets follow. Those octets need to be combined + // to form a size_t, so the number of octets to follow (length_len) + // must be small enough so that they fit in a size_t. + return false; + } + uint8_t length_byte; + for (size_t i = 0; i < length_len; i++) { + if (!reader.ReadByte(&length_byte)) + return false; + // A first length byte of all zeroes means the length was not encoded in + // minimum length. + if (i == 0 && length_byte == 0) + return false; + value_len <<= 8; + value_len += length_byte; + } + if (value_len < 0x80) { + // If value_len is < 0x80, then it could have been encoded in a single + // byte, meaning it was not encoded in minimum length. + return false; + } + } + + if (!reader.ReadBytes(value_len, out)) + return false; + advance_mark_ = reader.NewMark(); + *tag = tag_byte; + return true; +} + +bool Parser::Advance() { + if (advance_mark_.IsEmpty()) + return false; + if (!input_.AdvanceToMark(advance_mark_)) + return false; + advance_mark_ = Mark::NullMark(); + return true; +} + +bool Parser::HasMore() { + return input_.HasMore(); +} + +bool Parser::ReadRawTLV(Input* out) { + Tag tag; + Input value; + if (!PeekTagAndValue(&tag, &value)) + return false; + if (!input_.ReadToMark(advance_mark_, out)) + return false; + advance_mark_ = Mark::NullMark(); + + return true; +} + +bool Parser::ReadTagAndValue(Tag* tag, Input* out) { + if (!PeekTagAndValue(tag, out)) + return false; + CHECK(Advance()); + return true; +} + +bool Parser::ReadOptionalTag(Tag tag, Input* out, bool* present) { + if (!HasMore()) { + *present = false; + return true; + } + + Tag read_tag; + Input value; + if (!PeekTagAndValue(&read_tag, &value)) + return false; + *present = false; + if (read_tag == tag) { + *present = true; + *out = value; + CHECK(Advance()); + } else { + advance_mark_ = Mark::NullMark(); + } + return true; +} + +bool Parser::SkipOptionalTag(Tag tag, bool* present) { + Input out; + return ReadOptionalTag(tag, &out, present); +} + +bool Parser::ReadTag(Tag tag, Input* out) { + bool present; + return ReadOptionalTag(tag, out, &present) && present; +} + +bool Parser::SkipTag(Tag tag) { + Input out; + return ReadTag(tag, &out); +} + +// Type-specific variants of ReadTag + +bool Parser::ReadConstructed(Tag tag, Parser* out) { + if (!IsConstructed(tag)) + return false; + Input data; + if (!ReadTag(tag, &data)) + return false; + *out = Parser(data); + return true; +} + +bool Parser::ReadSequence(Parser* out) { + return ReadConstructed(kSequence, out); +} + +bool Parser::ReadUint64(uint64_t* out) { + Input encodedInt; + if (!ReadTag(kInteger, &encodedInt)) + return false; + return ParseUint64(encodedInt, out); +} + +} // namespace der + +} // namespace net
diff --git a/net/der/parser.h b/net/der/parser.h new file mode 100644 index 0000000..4e60239b --- /dev/null +++ b/net/der/parser.h
@@ -0,0 +1,177 @@ +// 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 NET_DER_PARSER_H_ +#define NET_DER_PARSER_H_ + +#include "base/compiler_specific.h" +#include "base/time/time.h" +#include "net/base/net_export.h" +#include "net/der/input.h" +#include "net/der/tag.h" + +namespace net { + +namespace der { + +// Parses a DER-encoded ASN.1 structure. DER (distinguished encoding rules) +// encodes each data value with a tag, length, and value (TLV). The tag +// indicates the type of the ASN.1 value. Depending on the type of the value, +// it could contain arbitrary bytes, so the length of the value is encoded +// after the tag and before the value to indicate how many bytes of value +// follow. DER also defines how the values are encoded for particular types. +// +// This Parser places a few restrictions on the DER encoding it can parse. The +// largest restriction is that it only supports tags which have a tag number +// no greater than 30 - these are the tags that fit in a single octet. The +// second restriction is that the maximum length for a value that can be parsed +// is 4GB. Both of these restrictions should be fine for any reasonable input. +// +// The Parser class is mainly focused on parsing the TLV structure of DER +// encoding, and does not directly handle parsing primitive values (other +// functions in the net::der namespace are provided for this.) When a Parser +// is created, it is passed in a reference to the encoded data. Because the +// encoded data is not owned by the Parser, the data cannot change during the +// lifespan of the Parser. The Parser functions by keeping a pointer to the +// current TLV which starts at the beginning of the input and advancing through +// the input as each TLV is read. As such, a Parser instance is thread-unsafe. +// +// Most methods for using the Parser write the current tag and/or value to +// the output parameters provided and then advance the input to the next TLV. +// None of the methods explicitly expose the length because it is part of the +// value. All methods return a boolean indicating whether there was a parsing +// error with the current TLV. +// +// Some methods are provided in the Parser class as convenience to both read +// the current TLV from the input and also parse the DER encoded value, +// converting it to a corresponding C++ type. These methods simply combine +// ReadTag() with the appropriate ParseType() free function. +// +// The design of DER encoding allows for nested data structures with +// constructed values, where the value is a series of TLVs. The Parser class +// is not designed to traverse through a nested encoding from a single object, +// but it does facilitate parsing nested data structures through the +// convenience methods ReadSequence() and the more general ReadConstructed(), +// which provide the user with another Parser object to traverse the next +// level of TLVs. +// +// For a brief example of how to use the Parser, suppose we have the following +// ASN.1 type definition: +// +// Foo ::= SEQUENCE { +// bar OCTET STRING OPTIONAL, +// quux OCTET STRING } +// +// If we have a DER-encoded Foo in an Input |encoded_value|, the +// following code shows an example of how to parse the quux field from the +// encoded data. +// +// bool ReadQuux(const Input& encoded_value, Input* quux_out) { +// Parser parser(encoded_value); +// Parser foo_parser; +// if (!parser.ReadSequence(&foo_parser)) +// return false; +// if (!foo_parser->SkipOptionalTag(kOctetString)) +// return false; +// if (!foo_parser->ReadTag(kOctetString, &quux)) +// return false; +// return true; +// } +class NET_EXPORT Parser { + public: + // Default constructor; equivalent to calling Parser(Input()). This only + // exists so that a Parser can be stack allocated and passed in to + // ReadConstructed() and similar methods. + Parser(); + + // Creates a parser to parse over the data represented by input. This class + // assumes that the underlying data will not change over the lifetime of + // the Parser object. + explicit Parser(const Input& input); + + // Returns whether there is any more data left in the input to parse. This + // does not guarantee that the data is parseable. + bool HasMore(); + + // Reads the current TLV from the input and advances. If the tag or length + // encoding for the current value is invalid, this method returns false and + // does not advance the input. Otherwise, it returns true, putting the + // read tag in |tag| and the value in |out|. + bool ReadTagAndValue(Tag* tag, Input* out) WARN_UNUSED_RESULT; + + // Reads the current TLV from the input and advances. Unlike ReadTagAndValue + // where only the value is put in |out|, this puts the raw bytes from the + // tag, length, and value in |out|. + bool ReadRawTLV(Input* out) WARN_UNUSED_RESULT; + + // Basic methods for reading or skipping the current TLV, with an + // expectation of what the current tag should be. It should be possible + // to parse any structure with these 4 methods; convenience methods are also + // provided to make some cases easier. + + // If the current tag in the input is |tag|, it puts the corresponding value + // in |out|, sets |was_present| to true, and advances the input to the next + // TLV. If the current tag is something else, then |was_present| is set to + // false and the input is not advanced. Like ReadTagAndValue, it returns + // false if the encoding is invalid and does not advance the input. + bool ReadOptionalTag(Tag tag, + Input* out, + bool* was_present) WARN_UNUSED_RESULT; + + // Like ReadOptionalTag, but the value is discarded. + bool SkipOptionalTag(Tag tag, bool* was_present) WARN_UNUSED_RESULT; + + // If the current tag matches |tag|, it puts the current value in |out|, + // advances the input, and returns true. Otherwise, it returns false. + bool ReadTag(Tag tag, Input* out) WARN_UNUSED_RESULT; + + // Advances the input and returns true if the current tag matches |tag|; + // otherwise it returns false. + bool SkipTag(Tag tag) WARN_UNUSED_RESULT; + + // Convenience methods to combine parsing the TLV with parsing the DER + // encoding for a specific type. + + // Reads the current TLV from the input, checks that the tag matches |tag| + // and is a constructed tag, and creates a new Parser from the value. + bool ReadConstructed(Tag tag, Parser* out) WARN_UNUSED_RESULT; + + // A more specific form of ReadConstructed that expects the current tag + // to be 0x30 (SEQUENCE). + bool ReadSequence(Parser* out) WARN_UNUSED_RESULT; + + // Expects the current tag to be kInteger, and calls ParseUint64 on the + // current value. Note that DER-encoded integers are arbitrary precision, + // so this method will fail for valid input that represents an integer + // outside the range of an int64. + bool ReadUint64(uint64_t* out) WARN_UNUSED_RESULT; + + // Lower level methods. The previous methods couple reading data from the + // input with advancing the Parser's internal pointer to the next TLV; these + // lower level methods decouple those two steps into methods that read from + // the current TLV and a method that advances the internal pointer to the + // next TLV. + + // Reads the current TLV from the input, putting the tag in |tag| and the raw + // value in |out|, but does not advance the input. Returns true if the tag + // and length are successfully read and the output exists. + bool PeekTagAndValue(Tag* tag, Input* out) WARN_UNUSED_RESULT; + + // Advances the input to the next TLV. This method only needs to be called + // after PeekTagAndValue; all other methods will advance the input if they + // read something. + bool Advance(); + + private: + ByteReader input_; + Mark advance_mark_; + + DISALLOW_COPY(Parser); +}; + +} // namespace der + +} // namespace net + +#endif // NET_DER_PARSER_H_
diff --git a/net/der/parser_unittest.cc b/net/der/parser_unittest.cc new file mode 100644 index 0000000..6c0e177d --- /dev/null +++ b/net/der/parser_unittest.cc
@@ -0,0 +1,201 @@ +// 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/logging.h" +#include "base/numerics/safe_math.h" +#include "net/der/input.h" +#include "net/der/parse_values.h" +#include "net/der/parser.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace net { +namespace der { +namespace test { + +TEST(ParserTest, ConsumesAllBytesOfTLV) { + const uint8_t der[] = {0x04, 0x00}; + Parser parser(Input(der, sizeof(der))); + Tag tag; + Input value; + ASSERT_TRUE(parser.ReadTagAndValue(&tag, &value)); + ASSERT_EQ(0x04, tag); + ASSERT_FALSE(parser.HasMore()); +} + +TEST(ParserTest, CanReadRawTLV) { + const uint8_t der[] = {0x02, 0x01, 0x01}; + Parser parser(Input(der, sizeof(der))); + Input tlv; + ASSERT_TRUE(parser.ReadRawTLV(&tlv)); + ByteReader tlv_reader(tlv); + size_t tlv_len = tlv_reader.BytesLeft(); + ASSERT_EQ(3u, tlv_len); + Input tlv_data; + ASSERT_TRUE(tlv_reader.ReadBytes(tlv_len, &tlv_data)); + ASSERT_FALSE(parser.HasMore()); +} + +TEST(ParserTest, IgnoresContentsOfInnerValues) { + // This is a SEQUENCE which has one member. The member is another SEQUENCE + // with an invalid encoding - its length is too long. + const uint8_t der[] = {0x30, 0x02, 0x30, 0x7e}; + Parser parser(Input(der, sizeof(der))); + Tag tag; + Input value; + ASSERT_TRUE(parser.ReadTagAndValue(&tag, &value)); +} + +TEST(ParserTest, FailsIfLengthOverlapsAnotherTLV) { + // This DER encoding has 2 top-level TLV tuples. The first is a SEQUENCE; + // the second is an INTEGER. The SEQUENCE contains an INTEGER, but its length + // is longer than what it has contents for. + const uint8_t der[] = {0x30, 0x02, 0x02, 0x01, 0x02, 0x01, 0x01}; + Parser parser(Input(der, sizeof(der))); + + Parser inner_sequence; + ASSERT_TRUE(parser.ReadSequence(&inner_sequence)); + uint64_t int_value; + ASSERT_TRUE(parser.ReadUint64(&int_value)); + ASSERT_EQ(1u, int_value); + ASSERT_FALSE(parser.HasMore()); + + // Try to read the INTEGER from the SEQUENCE, which should fail. + Tag tag; + Input value; + ASSERT_FALSE(inner_sequence.ReadTagAndValue(&tag, &value)); +} + +TEST(ParserTest, CanSkipOptionalTagAtEndOfInput) { + const uint8_t der[] = {0x02, 0x01, 0x01}; + Parser parser(Input(der, sizeof(der))); + + Tag tag; + Input value; + ASSERT_TRUE(parser.ReadTagAndValue(&tag, &value)); + bool present; + ASSERT_TRUE(parser.ReadOptionalTag(0x02, &value, &present)); + ASSERT_FALSE(present); + ASSERT_FALSE(parser.HasMore()); +} + +TEST(ParserTest, SkipOptionalTagDoesntConsumePresentNonMatchingTLVs) { + const uint8_t der[] = {0x02, 0x01, 0x01}; + Parser parser(Input(der, sizeof(der))); + + bool present; + ASSERT_TRUE(parser.SkipOptionalTag(0x04, &present)); + ASSERT_FALSE(present); + ASSERT_TRUE(parser.SkipOptionalTag(0x02, &present)); + ASSERT_TRUE(present); + ASSERT_FALSE(parser.HasMore()); +} + +TEST(ParserTest, TagNumbersAboveThirtyUnsupported) { + // Context-specific class, tag number 31, length 0. + const uint8_t der[] = {0x9f, 0x1f, 0x00}; + Parser parser(Input(der, sizeof(der))); + + Tag tag; + Input value; + ASSERT_FALSE(parser.ReadTagAndValue(&tag, &value)); + ASSERT_TRUE(parser.HasMore()); +} + +TEST(ParserTest, IncompleteEncodingTagOnly) { + const uint8_t der[] = {0x01}; + Parser parser(Input(der, sizeof(der))); + + Tag tag; + Input value; + ASSERT_FALSE(parser.ReadTagAndValue(&tag, &value)); + ASSERT_TRUE(parser.HasMore()); +} + +TEST(ParserTest, IncompleteEncodingLengthTruncated) { + // Tag: octet string; length: long form, should have 2 total octets, but + // the last one is missing. (There's also no value.) + const uint8_t der[] = {0x04, 0x81}; + Parser parser(Input(der, sizeof(der))); + + Tag tag; + Input value; + ASSERT_FALSE(parser.ReadTagAndValue(&tag, &value)); + ASSERT_TRUE(parser.HasMore()); +} + +TEST(ParserTest, IncompleteEncodingValueShorterThanLength) { + // Tag: octet string; length: 2; value: first octet 'T', second octet missing. + const uint8_t der[] = {0x04, 0x02, 0x84}; + Parser parser(Input(der, sizeof(der))); + + Tag tag; + Input value; + ASSERT_FALSE(parser.ReadTagAndValue(&tag, &value)); + ASSERT_TRUE(parser.HasMore()); +} + +TEST(ParserTest, LengthMustBeEncodedWithMinimumNumberOfOctets) { + const uint8_t der[] = {0x01, 0x81, 0x01, 0x00}; + Parser parser(Input(der, sizeof(der))); + + Tag tag; + Input value; + ASSERT_FALSE(parser.ReadTagAndValue(&tag, &value)); + ASSERT_TRUE(parser.HasMore()); +} + +TEST(ParserTest, LengthMustNotHaveLeadingZeroes) { + // Tag: octet string; length: 3 bytes of length encoding a value of 128 + // (it should be encoded in only 2 bytes). Value: 128 bytes of 0. + const uint8_t der[] = { + 0x04, 0x83, 0x80, 0x81, 0x80, // group the 0s separately + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + Parser parser(Input(der, sizeof(der))); + + Tag tag; + Input value; + ASSERT_FALSE(parser.ReadTagAndValue(&tag, &value)); + ASSERT_TRUE(parser.HasMore()); +} + +TEST(ParserTest, ReadConstructedFailsForNonConstructedTags) { + // Tag number is for SEQUENCE, but the constructed bit isn't set. + const uint8_t der[] = {0x10, 0x00}; + Parser parser(Input(der, sizeof(der))); + + Tag expected_tag = 0x10; + Parser sequence_parser; + ASSERT_FALSE(parser.ReadConstructed(expected_tag, &sequence_parser)); + + // Check that we didn't fail above because of a tag mismatch or an improperly + // encoded TLV. + Input value; + ASSERT_TRUE(parser.ReadTag(expected_tag, &value)); + ASSERT_FALSE(parser.HasMore()); +} + +TEST(ParserTest, CannotAdvanceAfterReadOptionalTag) { + const uint8_t der[] = {0x02, 0x01, 0x01}; + Parser parser(Input(der, sizeof(der))); + + Input value; + bool present; + ASSERT_TRUE(parser.ReadOptionalTag(0x04, &value, &present)); + ASSERT_FALSE(present); + ASSERT_FALSE(parser.Advance()); +} + +} // namespace test +} // namespace der +} // namespace net
diff --git a/net/der/tag.cc b/net/der/tag.cc new file mode 100644 index 0000000..00ced68 --- /dev/null +++ b/net/der/tag.cc
@@ -0,0 +1,28 @@ +// 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/logging.h" +#include "net/der/tag.h" + +namespace net { + +namespace der { + +Tag ContextSpecificConstructed(uint8_t base) { + DCHECK_EQ(base, base & kTagNumberMask); + return (base & kTagNumberMask) | kTagConstructed | kTagContextSpecific; +} + +Tag ContextSpecificPrimitive(uint8_t base) { + DCHECK_EQ(base, base & kTagNumberMask); + return (base & kTagNumberMask) | kTagPrimitive | kTagContextSpecific; +} + +bool IsConstructed(Tag tag) { + return (tag & kTagConstructionMask) == kTagConstructed; +} + +} // namespace der + +} // namespace net
diff --git a/net/der/tag.h b/net/der/tag.h new file mode 100644 index 0000000..e9092cb --- /dev/null +++ b/net/der/tag.h
@@ -0,0 +1,63 @@ +// 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 NET_DER_TAG_H_ +#define NET_DER_TAG_H_ + +#include <stdint.h> + +#include "net/base/net_export.h" + +namespace net { + +namespace der { + +// This Tag type represents the identifier for an ASN.1 tag as encoded with DER. +// It follows the same bit-for-bit representation (including the class, tag +// number, and primitive/constructed bit) as DER. Constants are provided for +// universal class types, and functions are provided for building context +// specific tags. Tags can also be built from the provided constants and +// bitmasks. +using Tag = uint8_t; + +// Universal class primitive types +const Tag kBool = 0x01; +const Tag kInteger = 0x02; +const Tag kBitString = 0x03; +const Tag kOctetString = 0x04; +const Tag kNull = 0x05; +const Tag kOid = 0x06; +const Tag kUtf8String = 0x0C; +const Tag kPrintableString = 0x13; +const Tag kUtcTime = 0x17; +const Tag kGeneralizedTime = 0x18; + +// Universal class constructed types +const Tag kSequence = 0x30; +const Tag kSet = 0x31; + +// Primitive/constructed bits +const uint8_t kTagPrimitive = 0x00; +const uint8_t kTagConstructed = 0x20; + +// Tag classes +const uint8_t kTagUniversal = 0x00; +const uint8_t kTagApplication = 0x40; +const uint8_t kTagContextSpecific = 0x80; +const uint8_t kTagPrivate = 0xC0; + +// Masks for the 3 components of a tag (class, primitive/constructed, number) +const uint8_t kTagNumberMask = 0x1F; +const uint8_t kTagConstructionMask = 0x20; +const uint8_t kTagClassMask = 0xC0; + +NET_EXPORT Tag ContextSpecificConstructed(uint8_t base); +NET_EXPORT Tag ContextSpecificPrimitive(uint8_t base); +NET_EXPORT bool IsConstructed(Tag tag); + +} // namespace der + +} // namespace net + +#endif // NET_DER_TAG_H_
diff --git a/net/disk_cache/blockfile/backend_impl_v3.cc b/net/disk_cache/blockfile/backend_impl_v3.cc index 96157551..64016c4 100644 --- a/net/disk_cache/blockfile/backend_impl_v3.cc +++ b/net/disk_cache/blockfile/backend_impl_v3.cc
@@ -667,8 +667,8 @@ : background_queue_(background_queue), data_(NULL) { } - virtual int OpenNextEntry(Entry** next_entry, - const net::CompletionCallback& callback) override { + int OpenNextEntry(Entry** next_entry, + const net::CompletionCallback& callback) override { if (!background_queue_) return net::ERR_FAILED; background_queue_->OpenNextEntry(&data_, next_entry, callback);
diff --git a/net/disk_cache/blockfile/entry_impl.cc b/net/disk_cache/blockfile/entry_impl.cc index 76bf7c7..add38de 100644 --- a/net/disk_cache/blockfile/entry_impl.cc +++ b/net/disk_cache/blockfile/entry_impl.cc
@@ -62,7 +62,7 @@ void SyncCallback::OnFileIOComplete(int bytes_copied) { entry_->DecrementIoCount(); if (!callback_.is_null()) { - if (entry_->net_log().IsLogging()) { + if (entry_->net_log().GetCaptureMode().enabled()) { entry_->net_log().EndEvent( end_event_type_, disk_cache::CreateNetLogReadWriteCompleteCallback(bytes_copied)); @@ -315,7 +315,7 @@ int EntryImpl::ReadDataImpl(int index, int offset, IOBuffer* buf, int buf_len, const CompletionCallback& callback) { - if (net_log_.IsLogging()) { + if (net_log_.GetCaptureMode().enabled()) { net_log_.BeginEvent( net::NetLog::TYPE_ENTRY_READ_DATA, CreateNetLogReadWriteDataCallback(index, offset, buf_len, false)); @@ -323,7 +323,7 @@ int result = InternalReadData(index, offset, buf, buf_len, callback); - if (result != net::ERR_IO_PENDING && net_log_.IsLogging()) { + if (result != net::ERR_IO_PENDING && net_log_.GetCaptureMode().enabled()) { net_log_.EndEvent( net::NetLog::TYPE_ENTRY_READ_DATA, CreateNetLogReadWriteCompleteCallback(result)); @@ -334,7 +334,7 @@ int EntryImpl::WriteDataImpl(int index, int offset, IOBuffer* buf, int buf_len, const CompletionCallback& callback, bool truncate) { - if (net_log_.IsLogging()) { + if (net_log_.GetCaptureMode().enabled()) { net_log_.BeginEvent( net::NetLog::TYPE_ENTRY_WRITE_DATA, CreateNetLogReadWriteDataCallback(index, offset, buf_len, truncate)); @@ -343,7 +343,7 @@ int result = InternalWriteData(index, offset, buf, buf_len, callback, truncate); - if (result != net::ERR_IO_PENDING && net_log_.IsLogging()) { + if (result != net::ERR_IO_PENDING && net_log_.GetCaptureMode().enabled()) { net_log_.EndEvent( net::NetLog::TYPE_ENTRY_WRITE_DATA, CreateNetLogReadWriteCompleteCallback(result));
diff --git a/net/disk_cache/blockfile/entry_impl_v3.cc b/net/disk_cache/blockfile/entry_impl_v3.cc index 04e9ad9..78ef1b8 100644 --- a/net/disk_cache/blockfile/entry_impl_v3.cc +++ b/net/disk_cache/blockfile/entry_impl_v3.cc
@@ -547,7 +547,7 @@ int EntryImpl::ReadDataImpl(int index, int offset, IOBuffer* buf, int buf_len, const CompletionCallback& callback) { - if (net_log_.IsLogging()) { + if (net_log_.GetCaptureMode().enabled()) { net_log_.BeginEvent( net::NetLog::TYPE_ENTRY_READ_DATA, CreateNetLogReadWriteDataCallback(index, offset, buf_len, false)); @@ -555,7 +555,7 @@ int result = InternalReadData(index, offset, buf, buf_len, callback); - if (result != net::ERR_IO_PENDING && net_log_.IsLogging()) { + if (result != net::ERR_IO_PENDING && net_log_.GetCaptureMode().enabled()) { net_log_.EndEvent( net::NetLog::TYPE_ENTRY_READ_DATA, CreateNetLogReadWriteCompleteCallback(result)); @@ -586,7 +586,7 @@ int EntryImpl::WriteDataImpl(int index, int offset, IOBuffer* buf, int buf_len, const CompletionCallback& callback, bool truncate) { - if (net_log_.IsLogging()) { + if (net_log_.GetCaptureMode().enabled()) { net_log_.BeginEvent( net::NetLog::TYPE_ENTRY_WRITE_DATA, CreateNetLogReadWriteDataCallback(index, offset, buf_len, truncate)); @@ -595,7 +595,7 @@ int result = InternalWriteData(index, offset, buf, buf_len, callback, truncate); - if (result != net::ERR_IO_PENDING && net_log_.IsLogging()) { + if (result != net::ERR_IO_PENDING && net_log_.GetCaptureMode().enabled()) { net_log_.EndEvent( net::NetLog::TYPE_ENTRY_WRITE_DATA, CreateNetLogReadWriteCompleteCallback(result));
diff --git a/net/disk_cache/blockfile/sparse_control.cc b/net/disk_cache/blockfile/sparse_control.cc index e5096dc..8ce659da0 100644 --- a/net/disk_cache/blockfile/sparse_control.cc +++ b/net/disk_cache/blockfile/sparse_control.cc
@@ -161,7 +161,7 @@ void LogChildOperationEnd(const net::BoundNetLog& net_log, disk_cache::SparseControl::SparseOperation operation, int result) { - if (net_log.IsLogging()) { + if (net_log.GetCaptureMode().enabled()) { net::NetLog::EventType event_type; switch (operation) { case disk_cache::SparseControl::kReadOperation: @@ -275,7 +275,7 @@ finished_ = false; abort_ = false; - if (entry_->net_log().IsLogging()) { + if (entry_->net_log().GetCaptureMode().enabled()) { entry_->net_log().BeginEvent( GetSparseEventType(operation_), CreateNetLogSparseOperationCallback(offset_, buf_len_)); @@ -679,14 +679,14 @@ // Range operations are finished synchronously, often without setting // |finished_| to true. if (kGetRangeOperation == operation_ && - entry_->net_log().IsLogging()) { + entry_->net_log().GetCaptureMode().enabled()) { entry_->net_log().EndEvent( net::NetLog::TYPE_SPARSE_GET_RANGE, CreateNetLogGetAvailableRangeResultCallback(offset_, result_)); } if (finished_) { if (kGetRangeOperation != operation_ && - entry_->net_log().IsLogging()) { + entry_->net_log().GetCaptureMode().enabled()) { entry_->net_log().EndEvent(GetSparseEventType(operation_)); } if (pending_) @@ -716,7 +716,7 @@ int rv = 0; switch (operation_) { case kReadOperation: - if (entry_->net_log().IsLogging()) { + if (entry_->net_log().GetCaptureMode().enabled()) { entry_->net_log().BeginEvent( net::NetLog::TYPE_SPARSE_READ_CHILD_DATA, CreateNetLogSparseReadWriteCallback(child_->net_log().source(), @@ -726,7 +726,7 @@ child_len_, callback); break; case kWriteOperation: - if (entry_->net_log().IsLogging()) { + if (entry_->net_log().GetCaptureMode().enabled()) { entry_->net_log().BeginEvent( net::NetLog::TYPE_SPARSE_WRITE_CHILD_DATA, CreateNetLogSparseReadWriteCallback(child_->net_log().source(), @@ -856,7 +856,7 @@ // We'll return the current result of the operation, which may be less than // the bytes to read or write, but the user cancelled the operation. abort_ = false; - if (entry_->net_log().IsLogging()) { + if (entry_->net_log().GetCaptureMode().enabled()) { entry_->net_log().AddEvent(net::NetLog::TYPE_CANCELLED); entry_->net_log().EndEvent(GetSparseEventType(operation_)); }
diff --git a/net/disk_cache/blockfile/sparse_control_v3.cc b/net/disk_cache/blockfile/sparse_control_v3.cc index cba0ed5..91492b3 100644 --- a/net/disk_cache/blockfile/sparse_control_v3.cc +++ b/net/disk_cache/blockfile/sparse_control_v3.cc
@@ -56,7 +56,7 @@ ChildrenDeleter(disk_cache::BackendImpl* backend, const std::string& name) : backend_(backend->GetWeakPtr()), name_(name), signature_(0) {} - virtual void OnFileIOComplete(int bytes_copied) override; + void OnFileIOComplete(int bytes_copied) override; // Two ways of deleting the children: if we have the children map, use Start() // directly, otherwise pass the data address to ReadData(). @@ -65,7 +65,7 @@ private: friend class base::RefCounted<ChildrenDeleter>; - virtual ~ChildrenDeleter() {} + ~ChildrenDeleter() override {} void DeleteChildren(); @@ -163,7 +163,7 @@ void LogChildOperationEnd(const net::BoundNetLog& net_log, disk_cache::SparseControl::SparseOperation operation, int result) { - if (net_log.IsLogging()) { + if (net_log.GetCaptureMode().enabled()) { net::NetLog::EventType event_type; switch (operation) { case disk_cache::SparseControl::kReadOperation: @@ -254,7 +254,7 @@ finished_ = false; abort_ = false; - if (entry_->net_log().IsLogging()) { + if (entry_->net_log().GetCaptureMode().enabled()) { entry_->net_log().BeginEvent( GetSparseEventType(operation_), CreateNetLogSparseOperationCallback(offset_, buf_len_)); @@ -563,7 +563,7 @@ int rv = 0; switch (operation_) { case kReadOperation: - if (entry_->net_log().IsLogging()) { + if (entry_->net_log().GetCaptureMode().enabled()) { entry_->net_log().BeginEvent( net::NetLog::TYPE_SPARSE_READ_CHILD_DATA, CreateNetLogSparseReadWriteCallback(child_->net_log().source(), @@ -573,7 +573,7 @@ child_len_, callback); break; case kWriteOperation: - if (entry_->net_log().IsLogging()) { + if (entry_->net_log().GetCaptureMode().enabled()) { entry_->net_log().BeginEvent( net::NetLog::TYPE_SPARSE_WRITE_CHILD_DATA, CreateNetLogSparseReadWriteCallback(child_->net_log().source(), @@ -846,7 +846,7 @@ // We'll return the current result of the operation, which may be less than // the bytes to read or write, but the user cancelled the operation. abort_ = false; - if (entry_->net_log().IsLogging()) { + if (entry_->net_log().GetCaptureMode().enabled()) { entry_->net_log().AddEvent(net::NetLog::TYPE_CANCELLED); entry_->net_log().EndEvent(GetSparseEventType(operation_)); }
diff --git a/net/disk_cache/memory/mem_entry_impl.cc b/net/disk_cache/memory/mem_entry_impl.cc index 7c6199b..c1dc8cf 100644 --- a/net/disk_cache/memory/mem_entry_impl.cc +++ b/net/disk_cache/memory/mem_entry_impl.cc
@@ -48,7 +48,7 @@ base::Value* NetLogChildEntryCreationCallback( const disk_cache::MemEntryImpl* parent, int child_id, - net::NetLog::LogLevel /* log_level */) { + net::NetLogCaptureMode /* capture_mode */) { base::DictionaryValue* dict = new base::DictionaryValue(); dict->SetString("key", GenerateChildName(parent->GetKey(), child_id)); dict->SetBoolean("created", true); @@ -185,7 +185,7 @@ int MemEntryImpl::ReadData(int index, int offset, IOBuffer* buf, int buf_len, const CompletionCallback& callback) { - if (net_log_.IsLogging()) { + if (net_log_.GetCaptureMode().enabled()) { net_log_.BeginEvent( net::NetLog::TYPE_ENTRY_READ_DATA, CreateNetLogReadWriteDataCallback(index, offset, buf_len, false)); @@ -193,7 +193,7 @@ int result = InternalReadData(index, offset, buf, buf_len); - if (net_log_.IsLogging()) { + if (net_log_.GetCaptureMode().enabled()) { net_log_.EndEvent( net::NetLog::TYPE_ENTRY_READ_DATA, CreateNetLogReadWriteCompleteCallback(result)); @@ -203,7 +203,7 @@ int MemEntryImpl::WriteData(int index, int offset, IOBuffer* buf, int buf_len, const CompletionCallback& callback, bool truncate) { - if (net_log_.IsLogging()) { + if (net_log_.GetCaptureMode().enabled()) { net_log_.BeginEvent( net::NetLog::TYPE_ENTRY_WRITE_DATA, CreateNetLogReadWriteDataCallback(index, offset, buf_len, truncate)); @@ -211,7 +211,7 @@ int result = InternalWriteData(index, offset, buf, buf_len, truncate); - if (net_log_.IsLogging()) { + if (net_log_.GetCaptureMode().enabled()) { net_log_.EndEvent( net::NetLog::TYPE_ENTRY_WRITE_DATA, CreateNetLogReadWriteCompleteCallback(result)); @@ -221,39 +221,39 @@ int MemEntryImpl::ReadSparseData(int64 offset, IOBuffer* buf, int buf_len, const CompletionCallback& callback) { - if (net_log_.IsLogging()) { + if (net_log_.GetCaptureMode().enabled()) { net_log_.BeginEvent( net::NetLog::TYPE_SPARSE_READ, CreateNetLogSparseOperationCallback(offset, buf_len)); } int result = InternalReadSparseData(offset, buf, buf_len); - if (net_log_.IsLogging()) + if (net_log_.GetCaptureMode().enabled()) net_log_.EndEvent(net::NetLog::TYPE_SPARSE_READ); return result; } int MemEntryImpl::WriteSparseData(int64 offset, IOBuffer* buf, int buf_len, const CompletionCallback& callback) { - if (net_log_.IsLogging()) { + if (net_log_.GetCaptureMode().enabled()) { net_log_.BeginEvent( net::NetLog::TYPE_SPARSE_WRITE, CreateNetLogSparseOperationCallback(offset, buf_len)); } int result = InternalWriteSparseData(offset, buf, buf_len); - if (net_log_.IsLogging()) + if (net_log_.GetCaptureMode().enabled()) net_log_.EndEvent(net::NetLog::TYPE_SPARSE_WRITE); return result; } int MemEntryImpl::GetAvailableRange(int64 offset, int len, int64* start, const CompletionCallback& callback) { - if (net_log_.IsLogging()) { + if (net_log_.GetCaptureMode().enabled()) { net_log_.BeginEvent( net::NetLog::TYPE_SPARSE_GET_RANGE, CreateNetLogSparseOperationCallback(offset, len)); } int result = GetAvailableRange(offset, len, start); - if (net_log_.IsLogging()) { + if (net_log_.GetCaptureMode().enabled()) { net_log_.EndEvent( net::NetLog::TYPE_SPARSE_GET_RANGE, CreateNetLogGetAvailableRangeResultCallback(*start, result)); @@ -373,7 +373,7 @@ // we should stop. if (child_offset < child->child_first_pos_) break; - if (net_log_.IsLogging()) { + if (net_log_.GetCaptureMode().enabled()) { net_log_.BeginEvent( net::NetLog::TYPE_SPARSE_READ_CHILD_DATA, CreateNetLogSparseReadWriteCallback(child->net_log().source(), @@ -381,7 +381,7 @@ } int ret = child->ReadData(kSparseData, child_offset, io_buf.get(), io_buf->BytesRemaining(), CompletionCallback()); - if (net_log_.IsLogging()) { + if (net_log_.GetCaptureMode().enabled()) { net_log_.EndEventWithNetErrorCode( net::NetLog::TYPE_SPARSE_READ_CHILD_DATA, ret); } @@ -430,7 +430,7 @@ // Keep a record of the last byte position (exclusive) in the child. int data_size = child->GetDataSize(kSparseData); - if (net_log_.IsLogging()) { + if (net_log_.GetCaptureMode().enabled()) { net_log_.BeginEvent( net::NetLog::TYPE_SPARSE_WRITE_CHILD_DATA, CreateNetLogSparseReadWriteCallback(child->net_log().source(), @@ -443,7 +443,7 @@ // continuous we may want to discard this write. int ret = child->WriteData(kSparseData, child_offset, io_buf.get(), write_len, CompletionCallback(), true); - if (net_log_.IsLogging()) { + if (net_log_.GetCaptureMode().enabled()) { net_log_.EndEventWithNetErrorCode( net::NetLog::TYPE_SPARSE_WRITE_CHILD_DATA, ret); }
diff --git a/net/disk_cache/net_log_parameters.cc b/net/disk_cache/net_log_parameters.cc index 5d7e50f..b13f916 100644 --- a/net/disk_cache/net_log_parameters.cc +++ b/net/disk_cache/net_log_parameters.cc
@@ -16,7 +16,7 @@ base::Value* NetLogEntryCreationCallback( const disk_cache::Entry* entry, bool created, - net::NetLog::LogLevel /* log_level */) { + net::NetLogCaptureMode /* capture_mode */) { base::DictionaryValue* dict = new base::DictionaryValue(); dict->SetString("key", entry->GetKey()); dict->SetBoolean("created", created); @@ -28,7 +28,7 @@ int offset, int buf_len, bool truncate, - net::NetLog::LogLevel /* log_level */) { + net::NetLogCaptureMode /* capture_mode */) { base::DictionaryValue* dict = new base::DictionaryValue(); dict->SetInteger("index", index); dict->SetInteger("offset", offset); @@ -40,7 +40,7 @@ base::Value* NetLogReadWriteCompleteCallback( int bytes_copied, - net::NetLog::LogLevel /* log_level */) { + net::NetLogCaptureMode /* capture_mode */) { DCHECK_NE(bytes_copied, net::ERR_IO_PENDING); base::DictionaryValue* dict = new base::DictionaryValue(); if (bytes_copied < 0) { @@ -54,7 +54,7 @@ base::Value* NetLogSparseOperationCallback( int64 offset, int buff_len, - net::NetLog::LogLevel /* log_level */) { + net::NetLogCaptureMode /* capture_mode */) { base::DictionaryValue* dict = new base::DictionaryValue(); // Values can only be created with at most 32-bit integers. Using a string // instead circumvents that restriction. @@ -66,7 +66,7 @@ base::Value* NetLogSparseReadWriteCallback( const net::NetLog::Source& source, int child_len, - net::NetLog::LogLevel /* log_level */) { + net::NetLogCaptureMode /* capture_mode */) { base::DictionaryValue* dict = new base::DictionaryValue(); source.AddToEventParameters(dict); dict->SetInteger("child_len", child_len); @@ -76,7 +76,7 @@ base::Value* NetLogGetAvailableRangeResultCallback( int64 start, int result, - net::NetLog::LogLevel /* log_level */) { + net::NetLogCaptureMode /* capture_mode */) { base::DictionaryValue* dict = new base::DictionaryValue(); if (result > 0) { dict->SetInteger("length", result);
diff --git a/net/disk_cache/simple/simple_entry_impl.cc b/net/disk_cache/simple/simple_entry_impl.cc index f8f1b85..a062dc2 100644 --- a/net/disk_cache/simple/simple_entry_impl.cc +++ b/net/disk_cache/simple/simple_entry_impl.cc
@@ -347,7 +347,7 @@ const CompletionCallback& callback) { DCHECK(io_thread_checker_.CalledOnValidThread()); - if (net_log_.IsLogging()) { + if (net_log_.GetCaptureMode().enabled()) { net_log_.AddEvent(net::NetLog::TYPE_SIMPLE_CACHE_ENTRY_READ_CALL, CreateNetLogReadWriteDataCallback(stream_index, offset, buf_len, false)); @@ -355,7 +355,7 @@ if (stream_index < 0 || stream_index >= kSimpleEntryStreamCount || buf_len < 0) { - if (net_log_.IsLogging()) { + if (net_log_.GetCaptureMode().enabled()) { net_log_.AddEvent(net::NetLog::TYPE_SIMPLE_CACHE_ENTRY_READ_END, CreateNetLogReadWriteCompleteCallback(net::ERR_INVALID_ARGUMENT)); } @@ -365,7 +365,7 @@ } if (pending_operations_.empty() && (offset >= GetDataSize(stream_index) || offset < 0 || !buf_len)) { - if (net_log_.IsLogging()) { + if (net_log_.GetCaptureMode().enabled()) { net_log_.AddEvent(net::NetLog::TYPE_SIMPLE_CACHE_ENTRY_READ_END, CreateNetLogReadWriteCompleteCallback(0)); } @@ -394,7 +394,7 @@ bool truncate) { DCHECK(io_thread_checker_.CalledOnValidThread()); - if (net_log_.IsLogging()) { + if (net_log_.GetCaptureMode().enabled()) { net_log_.AddEvent( net::NetLog::TYPE_SIMPLE_CACHE_ENTRY_WRITE_CALL, CreateNetLogReadWriteDataCallback(stream_index, offset, buf_len, @@ -403,7 +403,7 @@ if (stream_index < 0 || stream_index >= kSimpleEntryStreamCount || offset < 0 || buf_len < 0) { - if (net_log_.IsLogging()) { + if (net_log_.GetCaptureMode().enabled()) { net_log_.AddEvent( net::NetLog::TYPE_SIMPLE_CACHE_ENTRY_WRITE_END, CreateNetLogReadWriteCompleteCallback(net::ERR_INVALID_ARGUMENT)); @@ -412,7 +412,7 @@ return net::ERR_INVALID_ARGUMENT; } if (backend_.get() && offset + buf_len > backend_->GetMaxFileSize()) { - if (net_log_.IsLogging()) { + if (net_log_.GetCaptureMode().enabled()) { net_log_.AddEvent( net::NetLog::TYPE_SIMPLE_CACHE_ENTRY_WRITE_END, CreateNetLogReadWriteCompleteCallback(net::ERR_FAILED)); @@ -454,7 +454,7 @@ } op_callback = CompletionCallback(); ret_value = buf_len; - if (net_log_.IsLogging()) { + if (net_log_.GetCaptureMode().enabled()) { net_log_.AddEvent( net::NetLog::TYPE_SIMPLE_CACHE_ENTRY_WRITE_OPTIMISTIC, CreateNetLogReadWriteCompleteCallback(buf_len)); @@ -809,7 +809,7 @@ DCHECK(io_thread_checker_.CalledOnValidThread()); ScopedOperationRunner operation_runner(this); - if (net_log_.IsLogging()) { + if (net_log_.GetCaptureMode().enabled()) { net_log_.AddEvent( net::NetLog::TYPE_SIMPLE_CACHE_ENTRY_READ_BEGIN, CreateNetLogReadWriteDataCallback(stream_index, offset, buf_len, @@ -825,7 +825,7 @@ base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::Bind(callback, net::ERR_FAILED)); } - if (net_log_.IsLogging()) { + if (net_log_.GetCaptureMode().enabled()) { net_log_.AddEvent( net::NetLog::TYPE_SIMPLE_CACHE_ENTRY_READ_END, CreateNetLogReadWriteCompleteCallback(net::ERR_FAILED)); @@ -892,7 +892,7 @@ DCHECK(io_thread_checker_.CalledOnValidThread()); ScopedOperationRunner operation_runner(this); - if (net_log_.IsLogging()) { + if (net_log_.GetCaptureMode().enabled()) { net_log_.AddEvent( net::NetLog::TYPE_SIMPLE_CACHE_ENTRY_WRITE_BEGIN, CreateNetLogReadWriteDataCallback(stream_index, offset, buf_len, @@ -901,7 +901,7 @@ if (state_ == STATE_FAILURE || state_ == STATE_UNINITIALIZED) { RecordWriteResult(cache_type_, WRITE_RESULT_BAD_STATE); - if (net_log_.IsLogging()) { + if (net_log_.GetCaptureMode().enabled()) { net_log_.AddEvent( net::NetLog::TYPE_SIMPLE_CACHE_ENTRY_WRITE_END, CreateNetLogReadWriteCompleteCallback(net::ERR_FAILED)); @@ -1230,7 +1230,7 @@ crc_check_state_[stream_index] = CRC_CHECK_NOT_DONE; } } - if (net_log_.IsLogging()) { + if (net_log_.GetCaptureMode().enabled()) { net_log_.AddEvent( net::NetLog::TYPE_SIMPLE_CACHE_ENTRY_READ_END, CreateNetLogReadWriteCompleteCallback(*result)); @@ -1248,7 +1248,7 @@ RecordWriteResult(cache_type_, WRITE_RESULT_SUCCESS); else RecordWriteResult(cache_type_, WRITE_RESULT_SYNC_WRITE_FAILURE); - if (net_log_.IsLogging()) { + if (net_log_.GetCaptureMode().enabled()) { net_log_.AddEvent(net::NetLog::TYPE_SIMPLE_CACHE_ENTRY_WRITE_END, CreateNetLogReadWriteCompleteCallback(*result)); } @@ -1319,7 +1319,7 @@ DCHECK_EQ(STATE_IO_PENDING, state_); DCHECK(result); - if (net_log_.IsLogging()) { + if (net_log_.GetCaptureMode().enabled()) { net_log_.AddEventWithNetErrorCode( net::NetLog::TYPE_SIMPLE_CACHE_ENTRY_CHECKSUM_END, *result); @@ -1334,7 +1334,7 @@ } else { RecordReadResult(cache_type_, READ_RESULT_SYNC_CHECKSUM_FAILURE); } - if (net_log_.IsLogging()) { + if (net_log_.GetCaptureMode().enabled()) { net_log_.AddEvent(net::NetLog::TYPE_SIMPLE_CACHE_ENTRY_READ_END, CreateNetLogReadWriteCompleteCallback(*result)); }
diff --git a/net/disk_cache/simple/simple_net_log_parameters.cc b/net/disk_cache/simple/simple_net_log_parameters.cc index 4756c83..f3d9fe5 100644 --- a/net/disk_cache/simple/simple_net_log_parameters.cc +++ b/net/disk_cache/simple/simple_net_log_parameters.cc
@@ -17,7 +17,7 @@ base::Value* NetLogSimpleEntryConstructionCallback( const disk_cache::SimpleEntryImpl* entry, - net::NetLog::LogLevel log_level) { + net::NetLogCaptureMode capture_mode) { base::DictionaryValue* dict = new base::DictionaryValue(); dict->SetString("entry_hash", base::StringPrintf("%#016" PRIx64, entry->entry_hash())); @@ -27,7 +27,7 @@ base::Value* NetLogSimpleEntryCreationCallback( const disk_cache::SimpleEntryImpl* entry, int net_error, - net::NetLog::LogLevel /* log_level */) { + net::NetLogCaptureMode /* capture_mode */) { base::DictionaryValue* dict = new base::DictionaryValue(); dict->SetInteger("net_error", net_error); if (net_error == net::OK)
diff --git a/net/dns/dns_test_util.cc b/net/dns/dns_test_util.cc index aee3ba74..30e3d2e7 100644 --- a/net/dns/dns_test_util.cc +++ b/net/dns/dns_test_util.cc
@@ -60,15 +60,11 @@ } } - virtual const std::string& GetHostname() const override { - return hostname_; - } + const std::string& GetHostname() const override { return hostname_; } - virtual uint16 GetType() const override { - return qtype_; - } + uint16 GetType() const override { return qtype_; } - virtual void Start() override { + void Start() override { EXPECT_FALSE(started_); started_ = true; if (delayed_)
diff --git a/net/dns/dns_transaction.cc b/net/dns/dns_transaction.cc index 8c0b28b..0147092 100644 --- a/net/dns/dns_transaction.cc +++ b/net/dns/dns_transaction.cc
@@ -59,8 +59,8 @@ } base::Value* NetLogStartCallback(const std::string* hostname, - uint16 qtype, - NetLog::LogLevel /* log_level */) { + uint16 qtype, + NetLogCaptureMode /* capture_mode */) { base::DictionaryValue* dict = new base::DictionaryValue(); dict->SetString("hostname", *hostname); dict->SetInteger("query_type", qtype); @@ -98,7 +98,7 @@ // Returns a Value representing the received response, along with a reference // to the NetLog source source of the UDP socket used. The request must have // completed before this is called. - base::Value* NetLogResponseCallback(NetLog::LogLevel log_level) const { + base::Value* NetLogResponseCallback(NetLogCaptureMode capture_mode) const { DCHECK(GetResponse()->IsValid()); base::DictionaryValue* dict = new base::DictionaryValue();
diff --git a/net/dns/host_resolver_impl.cc b/net/dns/host_resolver_impl.cc index 35ea2fa2..5e050c6 100644 --- a/net/dns/host_resolver_impl.cc +++ b/net/dns/host_resolver_impl.cc
@@ -297,10 +297,11 @@ } // Creates NetLog parameters when the resolve failed. -base::Value* NetLogProcTaskFailedCallback(uint32 attempt_number, - int net_error, - int os_error, - NetLog::LogLevel /* log_level */) { +base::Value* NetLogProcTaskFailedCallback( + uint32 attempt_number, + int net_error, + int os_error, + NetLogCaptureMode /* capture_mode */) { base::DictionaryValue* dict = new base::DictionaryValue(); if (attempt_number) dict->SetInteger("attempt_number", attempt_number); @@ -332,7 +333,7 @@ // Creates NetLog parameters when the DnsTask failed. base::Value* NetLogDnsTaskFailedCallback(int net_error, int dns_error, - NetLog::LogLevel /* log_level */) { + NetLogCaptureMode /* capture_mode */) { base::DictionaryValue* dict = new base::DictionaryValue(); dict->SetInteger("net_error", net_error); if (dns_error) @@ -343,7 +344,7 @@ // Creates NetLog parameters containing the information in a RequestInfo object, // along with the associated NetLog::Source. base::Value* NetLogRequestInfoCallback(const HostResolver::RequestInfo* info, - NetLog::LogLevel /* log_level */) { + NetLogCaptureMode /* capture_mode */) { base::DictionaryValue* dict = new base::DictionaryValue(); dict->SetString("host", info->host_port_pair().ToString()); @@ -357,7 +358,7 @@ // Creates NetLog parameters for the creation of a HostResolverImpl::Job. base::Value* NetLogJobCreationCallback(const NetLog::Source& source, const std::string* host, - NetLog::LogLevel /* log_level */) { + NetLogCaptureMode /* capture_mode */) { base::DictionaryValue* dict = new base::DictionaryValue(); source.AddToEventParameters(dict); dict->SetString("host", *host); @@ -367,7 +368,7 @@ // Creates NetLog parameters for HOST_RESOLVER_IMPL_JOB_ATTACH/DETACH events. base::Value* NetLogJobAttachCallback(const NetLog::Source& source, RequestPriority priority, - NetLog::LogLevel /* log_level */) { + NetLogCaptureMode /* capture_mode */) { base::DictionaryValue* dict = new base::DictionaryValue(); source.AddToEventParameters(dict); dict->SetString("priority", RequestPriorityToString(priority)); @@ -376,7 +377,7 @@ // Creates NetLog parameters for the DNS_CONFIG_CHANGED event. base::Value* NetLogDnsConfigCallback(const DnsConfig* config, - NetLog::LogLevel /* log_level */) { + NetLogCaptureMode /* capture_mode */) { return config->ToValue(); }
diff --git a/net/dns/mock_mdns_socket_factory.h b/net/dns/mock_mdns_socket_factory.h index 6278661..c1536bc1 100644 --- a/net/dns/mock_mdns_socket_factory.h +++ b/net/dns/mock_mdns_socket_factory.h
@@ -15,7 +15,7 @@ class MockMDnsDatagramServerSocket : public DatagramServerSocket { public: explicit MockMDnsDatagramServerSocket(AddressFamily address_family); - ~MockMDnsDatagramServerSocket(); + ~MockMDnsDatagramServerSocket() override; // DatagramServerSocket implementation: MOCK_METHOD1(Listen, int(const IPEndPoint& address)); @@ -26,8 +26,10 @@ IPEndPoint* address, const CompletionCallback& callback)); - virtual int SendTo(IOBuffer* buf, int buf_len, const IPEndPoint& address, - const CompletionCallback& callback) override; + int SendTo(IOBuffer* buf, + int buf_len, + const IPEndPoint& address, + const CompletionCallback& callback) override; MOCK_METHOD3(SendToInternal, int(const std::string& packet, const std::string address, @@ -39,7 +41,7 @@ MOCK_METHOD0(Close, void()); MOCK_CONST_METHOD1(GetPeerAddress, int(IPEndPoint* address)); - virtual int GetLocalAddress(IPEndPoint* address) const override; + int GetLocalAddress(IPEndPoint* address) const override; MOCK_CONST_METHOD0(NetLog, const BoundNetLog&()); MOCK_METHOD0(AllowAddressReuse, void()); @@ -72,10 +74,9 @@ class MockMDnsSocketFactory : public MDnsSocketFactory { public: MockMDnsSocketFactory(); - virtual ~MockMDnsSocketFactory(); + ~MockMDnsSocketFactory() override; - virtual void CreateSockets( - ScopedVector<DatagramServerSocket>* sockets) override; + void CreateSockets(ScopedVector<DatagramServerSocket>* sockets) override; void SimulateReceive(const uint8* packet, int size);
diff --git a/net/filter/sdch_filter.cc b/net/filter/sdch_filter.cc index 6b52d8f..858e101 100644 --- a/net/filter/sdch_filter.cc +++ b/net/filter/sdch_filter.cc
@@ -93,7 +93,7 @@ base::Value* NetLogSdchResponseCorruptionDetectionCallback( ResponseCorruptionDetectionCause cause, bool cached, - NetLog::LogLevel log_level) { + NetLogCaptureMode capture_mode) { base::DictionaryValue* dict = new base::DictionaryValue(); dict->SetString("cause", ResponseCorruptionDetectionCauseToString(cause)); dict->SetBoolean("cached", cached);
diff --git a/net/ftp/ftp_ctrl_response_buffer.cc b/net/ftp/ftp_ctrl_response_buffer.cc index f7b14678..36486b7 100644 --- a/net/ftp/ftp_ctrl_response_buffer.cc +++ b/net/ftp/ftp_ctrl_response_buffer.cc
@@ -80,7 +80,7 @@ namespace { base::Value* NetLogFtpCtrlResponseCallback(const FtpCtrlResponse* response, - NetLog::LogLevel log_level) { + NetLogCaptureMode capture_mode) { base::ListValue* lines = new base::ListValue(); lines->AppendStrings(response->lines);
diff --git a/net/http/http_cache_transaction.cc b/net/http/http_cache_transaction.cc index 5d6dc4d..cebecda 100644 --- a/net/http/http_cache_transaction.cc +++ b/net/http/http_cache_transaction.cc
@@ -228,7 +228,7 @@ base::Value* NetLogAsyncRevalidationInfoCallback( const net::NetLog::Source& source, const net::HttpRequestInfo* request, - net::NetLog::LogLevel log_level) { + net::NetLogCaptureMode capture_mode) { base::DictionaryValue* dict = new base::DictionaryValue(); source.AddToEventParameters(dict); @@ -1645,7 +1645,7 @@ next_state_ = STATE_TRUNCATE_CACHED_DATA_COMPLETE; if (!entry_) return OK; - if (net_log_.IsLogging()) + if (net_log_.GetCaptureMode().enabled()) net_log_.BeginEvent(NetLog::TYPE_HTTP_CACHE_WRITE_DATA); // Truncate the stream. return WriteToEntry(kResponseContentIndex, 0, NULL, 0, io_callback_); @@ -1653,7 +1653,7 @@ int HttpCache::Transaction::DoTruncateCachedDataComplete(int result) { if (entry_) { - if (net_log_.IsLogging()) { + if (net_log_.GetCaptureMode().enabled()) { net_log_.EndEventWithNetErrorCode(NetLog::TYPE_HTTP_CACHE_WRITE_DATA, result); } @@ -1668,14 +1668,14 @@ if (!entry_) return OK; - if (net_log_.IsLogging()) + if (net_log_.GetCaptureMode().enabled()) net_log_.BeginEvent(NetLog::TYPE_HTTP_CACHE_WRITE_INFO); return WriteToEntry(kMetadataIndex, 0, NULL, 0, io_callback_); } int HttpCache::Transaction::DoTruncateCachedMetadataComplete(int result) { if (entry_) { - if (net_log_.IsLogging()) { + if (net_log_.GetCaptureMode().enabled()) { net_log_.EndEventWithNetErrorCode(NetLog::TYPE_HTTP_CACHE_WRITE_INFO, result); } @@ -1812,7 +1812,7 @@ "422516 HttpCache::Transaction::DoCacheWriteResponse")); if (entry_) { - if (net_log_.IsLogging()) + if (net_log_.GetCaptureMode().enabled()) net_log_.BeginEvent(NetLog::TYPE_HTTP_CACHE_WRITE_INFO); } return WriteResponseInfoToEntry(false); @@ -1820,7 +1820,7 @@ int HttpCache::Transaction::DoCacheWriteTruncatedResponse() { if (entry_) { - if (net_log_.IsLogging()) + if (net_log_.GetCaptureMode().enabled()) net_log_.BeginEvent(NetLog::TYPE_HTTP_CACHE_WRITE_INFO); } return WriteResponseInfoToEntry(true); @@ -1831,7 +1831,7 @@ target_state_ = STATE_NONE; if (!entry_) return OK; - if (net_log_.IsLogging()) { + if (net_log_.GetCaptureMode().enabled()) { net_log_.EndEventWithNetErrorCode(NetLog::TYPE_HTTP_CACHE_WRITE_INFO, result); } @@ -1883,7 +1883,7 @@ DCHECK(entry_); next_state_ = STATE_CACHE_READ_DATA_COMPLETE; - if (net_log_.IsLogging()) + if (net_log_.GetCaptureMode().enabled()) net_log_.BeginEvent(NetLog::TYPE_HTTP_CACHE_READ_DATA); if (partial_.get()) { return partial_->CacheRead(entry_->disk_entry, read_buf_.get(), io_buf_len_, @@ -1896,7 +1896,7 @@ } int HttpCache::Transaction::DoCacheReadDataComplete(int result) { - if (net_log_.IsLogging()) { + if (net_log_.GetCaptureMode().enabled()) { net_log_.EndEventWithNetErrorCode(NetLog::TYPE_HTTP_CACHE_READ_DATA, result); } @@ -1927,7 +1927,7 @@ next_state_ = STATE_CACHE_WRITE_DATA_COMPLETE; write_len_ = num_bytes; if (entry_) { - if (net_log_.IsLogging()) + if (net_log_.GetCaptureMode().enabled()) net_log_.BeginEvent(NetLog::TYPE_HTTP_CACHE_WRITE_DATA); } @@ -1936,7 +1936,7 @@ int HttpCache::Transaction::DoCacheWriteDataComplete(int result) { if (entry_) { - if (net_log_.IsLogging()) { + if (net_log_.GetCaptureMode().enabled()) { net_log_.EndEventWithNetErrorCode(NetLog::TYPE_HTTP_CACHE_WRITE_DATA, result); } @@ -2758,7 +2758,7 @@ if ((response_.headers->HasHeaderValue("cache-control", "no-store")) || net::IsCertStatusError(response_.ssl_info.cert_status)) { DoneWritingToEntry(false); - if (net_log_.IsLogging()) + if (net_log_.GetCaptureMode().enabled()) net_log_.EndEvent(NetLog::TYPE_HTTP_CACHE_WRITE_INFO); return OK; }
diff --git a/net/http/http_log_util.cc b/net/http/http_log_util.cc index 7e93911..0467fb605a 100644 --- a/net/http/http_log_util.cc +++ b/net/http/http_log_util.cc
@@ -34,15 +34,14 @@ } // namespace -std::string ElideHeaderValueForNetLog(NetLog::LogLevel log_level, +std::string ElideHeaderValueForNetLog(NetLogCaptureMode capture_mode, const std::string& header, const std::string& value) { std::string::const_iterator redact_begin = value.begin(); std::string::const_iterator redact_end = value.begin(); if (redact_begin == redact_end && - log_level >= NetLog::LOG_STRIP_PRIVATE_DATA) { - + !capture_mode.include_cookies_and_credentials()) { // Note: this logic should be kept in sync with stripCookiesAndLoginInfo in // chrome/browser/resources/net_internals/log_view_painter.js.
diff --git a/net/http/http_log_util.h b/net/http/http_log_util.h index 6894809..7758943 100644 --- a/net/http/http_log_util.h +++ b/net/http/http_log_util.h
@@ -15,7 +15,7 @@ // Given an HTTP header |header| with value |value|, returns the elided version // of the header value at |log_level|. NET_EXPORT_PRIVATE std::string ElideHeaderValueForNetLog( - NetLog::LogLevel log_level, + NetLogCaptureMode capture_mode, const std::string& header, const std::string& value);
diff --git a/net/http/http_log_util_unittest.cc b/net/http/http_log_util_unittest.cc index dd6af69..bd9c79f 100644 --- a/net/http/http_log_util_unittest.cc +++ b/net/http/http_log_util_unittest.cc
@@ -9,50 +9,63 @@ TEST(HttpLogUtilTest, ElideHeaderValueForNetLog) { // Only elide for appropriate log level. - EXPECT_EQ("[10 bytes were stripped]", ElideHeaderValueForNetLog( - NetLog::LOG_STRIP_PRIVATE_DATA, "Cookie", "name=value")); + EXPECT_EQ("[10 bytes were stripped]", + ElideHeaderValueForNetLog(NetLogCaptureMode::Default(), "Cookie", + "name=value")); EXPECT_EQ("name=value", ElideHeaderValueForNetLog( - NetLog::LOG_ALL_BUT_BYTES, "Cookie", "name=value")); + NetLogCaptureMode::IncludeCookiesAndCredentials(), + "Cookie", "name=value")); // Headers are compared case insensitively. - EXPECT_EQ("[10 bytes were stripped]", ElideHeaderValueForNetLog( - NetLog::LOG_STRIP_PRIVATE_DATA, "cOoKiE", "name=value")); + EXPECT_EQ("[10 bytes were stripped]", + ElideHeaderValueForNetLog(NetLogCaptureMode::Default(), "cOoKiE", + "name=value")); // These headers should be completely elided. - EXPECT_EQ("[10 bytes were stripped]", ElideHeaderValueForNetLog( - NetLog::LOG_STRIP_PRIVATE_DATA, "Set-Cookie", "name=value")); - EXPECT_EQ("[10 bytes were stripped]", ElideHeaderValueForNetLog( - NetLog::LOG_STRIP_PRIVATE_DATA, "Set-Cookie2", "name=value")); - EXPECT_EQ("[10 bytes were stripped]", ElideHeaderValueForNetLog( - NetLog::LOG_STRIP_PRIVATE_DATA, "Authorization", "Basic 1234")); - EXPECT_EQ("[10 bytes were stripped]", ElideHeaderValueForNetLog( - NetLog::LOG_STRIP_PRIVATE_DATA, "Proxy-Authorization", "Basic 1234")); + EXPECT_EQ("[10 bytes were stripped]", + ElideHeaderValueForNetLog(NetLogCaptureMode::Default(), + "Set-Cookie", "name=value")); + EXPECT_EQ("[10 bytes were stripped]", + ElideHeaderValueForNetLog(NetLogCaptureMode::Default(), + "Set-Cookie2", "name=value")); + EXPECT_EQ("[10 bytes were stripped]", + ElideHeaderValueForNetLog(NetLogCaptureMode::Default(), + "Authorization", "Basic 1234")); + EXPECT_EQ("[10 bytes were stripped]", + ElideHeaderValueForNetLog(NetLogCaptureMode::Default(), + "Proxy-Authorization", "Basic 1234")); // Unknown headers should pass through. - EXPECT_EQ("value", ElideHeaderValueForNetLog( - NetLog::LOG_STRIP_PRIVATE_DATA, "Boring", "value")); + EXPECT_EQ("value", ElideHeaderValueForNetLog(NetLogCaptureMode::Default(), + "Boring", "value")); // Basic and Digest auth challenges are public. + EXPECT_EQ("Basic realm=test", + ElideHeaderValueForNetLog(NetLogCaptureMode::Default(), + "WWW-Authenticate", "Basic realm=test")); + EXPECT_EQ("Digest realm=test", + ElideHeaderValueForNetLog(NetLogCaptureMode::Default(), + "WWW-Authenticate", "Digest realm=test")); EXPECT_EQ("Basic realm=test", ElideHeaderValueForNetLog( - NetLog::LOG_STRIP_PRIVATE_DATA, "WWW-Authenticate", "Basic realm=test")); - EXPECT_EQ("Digest realm=test", ElideHeaderValueForNetLog( - NetLog::LOG_STRIP_PRIVATE_DATA, "WWW-Authenticate", "Digest realm=test")); - EXPECT_EQ("Basic realm=test", ElideHeaderValueForNetLog( - NetLog::LOG_STRIP_PRIVATE_DATA, - "Proxy-Authenticate", "Basic realm=test")); - EXPECT_EQ("Digest realm=test", ElideHeaderValueForNetLog( - NetLog::LOG_STRIP_PRIVATE_DATA, - "Proxy-Authenticate", "Digest realm=test")); + NetLogCaptureMode::Default(), + "Proxy-Authenticate", "Basic realm=test")); + EXPECT_EQ( + "Digest realm=test", + ElideHeaderValueForNetLog(NetLogCaptureMode::Default(), + "Proxy-Authenticate", "Digest realm=test")); // Multi-round mechanisms partially elided. - EXPECT_EQ("NTLM [4 bytes were stripped]", ElideHeaderValueForNetLog( - NetLog::LOG_STRIP_PRIVATE_DATA, "WWW-Authenticate", "NTLM 1234")); - EXPECT_EQ("NTLM [4 bytes were stripped]", ElideHeaderValueForNetLog( - NetLog::LOG_STRIP_PRIVATE_DATA, "Proxy-Authenticate", "NTLM 1234")); + EXPECT_EQ("NTLM [4 bytes were stripped]", + ElideHeaderValueForNetLog(NetLogCaptureMode::Default(), + "WWW-Authenticate", "NTLM 1234")); + EXPECT_EQ("NTLM [4 bytes were stripped]", + ElideHeaderValueForNetLog(NetLogCaptureMode::Default(), + "Proxy-Authenticate", "NTLM 1234")); // Leave whitespace intact. - EXPECT_EQ("NTLM [4 bytes were stripped] ", ElideHeaderValueForNetLog( - NetLog::LOG_STRIP_PRIVATE_DATA, "WWW-Authenticate", "NTLM 1234 ")); + EXPECT_EQ("NTLM [4 bytes were stripped] ", + ElideHeaderValueForNetLog(NetLogCaptureMode::Default(), + "WWW-Authenticate", "NTLM 1234 ")); } } // namspace net
diff --git a/net/http/http_network_transaction.cc b/net/http/http_network_transaction.cc index b5e5d5c..3e9074ac 100644 --- a/net/http/http_network_transaction.cc +++ b/net/http/http_network_transaction.cc
@@ -97,7 +97,7 @@ int net_error, uint16 version_before, uint16 version_after, - NetLog::LogLevel /* log_level */) { + NetLogCaptureMode /* capture_mode */) { base::DictionaryValue* dict = new base::DictionaryValue(); dict->SetString("host_and_port", GetHostAndPort(*url)); dict->SetInteger("net_error", net_error); @@ -106,9 +106,10 @@ return dict; } -base::Value* NetLogSSLCipherFallbackCallback(const GURL* url, - int net_error, - NetLog::LogLevel /* log_level */) { +base::Value* NetLogSSLCipherFallbackCallback( + const GURL* url, + int net_error, + NetLogCaptureMode /* capture_mode */) { base::DictionaryValue* dict = new base::DictionaryValue(); dict->SetString("host_and_port", GetHostAndPort(*url)); dict->SetInteger("net_error", net_error);
diff --git a/net/http/http_network_transaction_unittest.cc b/net/http/http_network_transaction_unittest.cc index eb81161..a1bba94e 100644 --- a/net/http/http_network_transaction_unittest.cc +++ b/net/http/http_network_transaction_unittest.cc
@@ -219,7 +219,7 @@ void AddWebSocketHeaders(net::HttpRequestHeaders* headers) { headers->SetHeader("Connection", "Upgrade"); headers->SetHeader("Upgrade", "websocket"); - headers->SetHeader("Origin", "http://www.google.com"); + headers->SetHeader("Origin", "http://www.example.org"); headers->SetHeader("Sec-WebSocket-Version", "13"); headers->SetHeader("Sec-WebSocket-Key", "dGhlIHNhbXBsZSBub25jZQ=="); } @@ -314,7 +314,7 @@ HttpRequestInfo request; request.method = "GET"; - request.url = GURL("http://www.google.com/"); + request.url = GURL("http://www.example.org/"); request.load_flags = 0; BoundTestNetLog log; @@ -329,7 +329,7 @@ TestCompletionCallback callback; - EXPECT_TRUE(log.bound().IsLogging()); + EXPECT_TRUE(log.bound().GetCaptureMode().enabled()); int rv = trans->Start(&request, callback.callback(), log.bound()); EXPECT_EQ(ERR_IO_PENDING, rv); @@ -376,13 +376,13 @@ EXPECT_TRUE(trans->GetFullRequestHeaders(&request_headers)); std::string value; EXPECT_TRUE(request_headers.GetHeader("Host", &value)); - EXPECT_EQ("www.google.com", value); + EXPECT_EQ("www.example.org", value); EXPECT_TRUE(request_headers.GetHeader("Connection", &value)); EXPECT_EQ("keep-alive", value); std::string response_headers; EXPECT_TRUE(GetHeaders(entries[pos].params.get(), &response_headers)); - EXPECT_EQ("['Host: www.google.com','Connection: keep-alive']", + EXPECT_EQ("['Host: www.example.org','Connection: keep-alive']", response_headers); out.totalReceivedBytes = trans->GetTotalReceivedBytes(); @@ -613,7 +613,7 @@ if (!auth_challenge) return false; EXPECT_FALSE(auth_challenge->is_proxy); - EXPECT_EQ("www.google.com:80", auth_challenge->challenger.ToString()); + EXPECT_EQ("www.example.org:80", auth_challenge->challenger.ToString()); EXPECT_EQ("MyRealm1", auth_challenge->realm); EXPECT_EQ("basic", auth_challenge->scheme); return true; @@ -633,7 +633,7 @@ if (!auth_challenge) return false; EXPECT_FALSE(auth_challenge->is_proxy); - EXPECT_EQ("www.google.com:80", auth_challenge->challenger.ToString()); + EXPECT_EQ("www.example.org:80", auth_challenge->challenger.ToString()); EXPECT_EQ("digestive", auth_challenge->realm); EXPECT_EQ("digest", auth_challenge->scheme); return true; @@ -1012,7 +1012,7 @@ TEST_P(HttpNetworkTransactionTest, Head) { HttpRequestInfo request; request.method = "HEAD"; - request.url = GURL("http://www.google.com/"); + request.url = GURL("http://www.example.org/"); request.load_flags = 0; scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps_)); @@ -1024,10 +1024,11 @@ base::Unretained(&proxy_headers_handler))); MockWrite data_writes1[] = { - MockWrite("HEAD / HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Connection: keep-alive\r\n" - "Content-Length: 0\r\n\r\n"), + MockWrite( + "HEAD / HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Connection: keep-alive\r\n" + "Content-Length: 0\r\n\r\n"), }; MockRead data_reads1[] = { MockRead("HTTP/1.1 404 Not Found\r\n"), @@ -1095,7 +1096,7 @@ for (int i = 0; i < 2; ++i) { HttpRequestInfo request; request.method = "GET"; - request.url = GURL("http://www.google.com/"); + request.url = GURL("http://www.example.org/"); request.load_flags = 0; scoped_ptr<HttpTransaction> trans( @@ -1558,7 +1559,7 @@ TEST_P(HttpNetworkTransactionTest, NonKeepAliveConnectionReset) { HttpRequestInfo request; request.method = "GET"; - request.url = GURL("http://www.google.com/"); + request.url = GURL("http://www.example.org/"); request.load_flags = 0; scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps_)); @@ -1611,7 +1612,7 @@ TEST_P(HttpNetworkTransactionTest, ThrottleBeforeNetworkStart) { HttpRequestInfo request; request.method = "GET"; - request.url = GURL("http://www.google.com/"); + request.url = GURL("http://www.example.org/"); request.load_flags = 0; scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps_)); @@ -1661,7 +1662,7 @@ TEST_P(HttpNetworkTransactionTest, ThrottleAndCancelBeforeNetworkStart) { HttpRequestInfo request; request.method = "GET"; - request.url = GURL("http://www.google.com/"); + request.url = GURL("http://www.example.org/"); request.load_flags = 0; scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps_)); @@ -1693,7 +1694,7 @@ TEST_P(HttpNetworkTransactionTest, KeepAliveEarlyClose) { HttpRequestInfo request; request.method = "GET"; - request.url = GURL("http://www.google.com/"); + request.url = GURL("http://www.example.org/"); request.load_flags = 0; scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps_)); @@ -1734,7 +1735,7 @@ TEST_P(HttpNetworkTransactionTest, KeepAliveEarlyClose2) { HttpRequestInfo request; request.method = "GET"; - request.url = GURL("http://www.google.com/"); + request.url = GURL("http://www.example.org/"); request.load_flags = 0; scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps_)); @@ -1883,7 +1884,7 @@ TEST_P(HttpNetworkTransactionTest, BasicAuth) { HttpRequestInfo request; request.method = "GET"; - request.url = GURL("http://www.google.com/"); + request.url = GURL("http://www.example.org/"); request.load_flags = 0; TestNetLog log; @@ -1893,9 +1894,10 @@ new HttpNetworkTransaction(DEFAULT_PRIORITY, session.get())); MockWrite data_writes1[] = { - MockWrite("GET / HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Connection: keep-alive\r\n\r\n"), + MockWrite( + "GET / HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Connection: keep-alive\r\n\r\n"), }; MockRead data_reads1[] = { @@ -1914,10 +1916,11 @@ // After calling trans->RestartWithAuth(), this is the request we should // be issuing -- the final header line contains the credentials. MockWrite data_writes2[] = { - MockWrite("GET / HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Connection: keep-alive\r\n" - "Authorization: Basic Zm9vOmJhcg==\r\n\r\n"), + MockWrite( + "GET / HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Connection: keep-alive\r\n" + "Authorization: Basic Zm9vOmJhcg==\r\n\r\n"), }; // Lastly, the server responds with the actual content. @@ -1984,7 +1987,7 @@ TEST_P(HttpNetworkTransactionTest, DoNotSendAuth) { HttpRequestInfo request; request.method = "GET"; - request.url = GURL("http://www.google.com/"); + request.url = GURL("http://www.example.org/"); request.load_flags = net::LOAD_DO_NOT_SEND_AUTH_DATA; scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps_)); @@ -1992,9 +1995,10 @@ new HttpNetworkTransaction(DEFAULT_PRIORITY, session.get())); MockWrite data_writes[] = { - MockWrite("GET / HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Connection: keep-alive\r\n\r\n"), + MockWrite( + "GET / HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Connection: keep-alive\r\n\r\n"), }; MockRead data_reads[] = { @@ -2030,7 +2034,7 @@ TEST_P(HttpNetworkTransactionTest, BasicAuthKeepAlive) { HttpRequestInfo request; request.method = "GET"; - request.url = GURL("http://www.google.com/"); + request.url = GURL("http://www.example.org/"); request.load_flags = 0; TestNetLog log; @@ -2038,16 +2042,18 @@ scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps_)); MockWrite data_writes1[] = { - MockWrite("GET / HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Connection: keep-alive\r\n\r\n"), + MockWrite( + "GET / HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Connection: keep-alive\r\n\r\n"), - // After calling trans->RestartWithAuth(), this is the request we should - // be issuing -- the final header line contains the credentials. - MockWrite("GET / HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Connection: keep-alive\r\n" - "Authorization: Basic Zm9vOmJhcg==\r\n\r\n"), + // After calling trans->RestartWithAuth(), this is the request we should + // be issuing -- the final header line contains the credentials. + MockWrite( + "GET / HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Connection: keep-alive\r\n" + "Authorization: Basic Zm9vOmJhcg==\r\n\r\n"), }; MockRead data_reads1[] = { @@ -2130,22 +2136,24 @@ TEST_P(HttpNetworkTransactionTest, BasicAuthKeepAliveNoBody) { HttpRequestInfo request; request.method = "GET"; - request.url = GURL("http://www.google.com/"); + request.url = GURL("http://www.example.org/"); request.load_flags = 0; scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps_)); MockWrite data_writes1[] = { - MockWrite("GET / HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Connection: keep-alive\r\n\r\n"), + MockWrite( + "GET / HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Connection: keep-alive\r\n\r\n"), - // After calling trans->RestartWithAuth(), this is the request we should - // be issuing -- the final header line contains the credentials. - MockWrite("GET / HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Connection: keep-alive\r\n" - "Authorization: Basic Zm9vOmJhcg==\r\n\r\n"), + // After calling trans->RestartWithAuth(), this is the request we should + // be issuing -- the final header line contains the credentials. + MockWrite( + "GET / HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Connection: keep-alive\r\n" + "Authorization: Basic Zm9vOmJhcg==\r\n\r\n"), }; MockRead data_reads1[] = { @@ -2206,22 +2214,24 @@ TEST_P(HttpNetworkTransactionTest, BasicAuthKeepAliveLargeBody) { HttpRequestInfo request; request.method = "GET"; - request.url = GURL("http://www.google.com/"); + request.url = GURL("http://www.example.org/"); request.load_flags = 0; scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps_)); MockWrite data_writes1[] = { - MockWrite("GET / HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Connection: keep-alive\r\n\r\n"), + MockWrite( + "GET / HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Connection: keep-alive\r\n\r\n"), - // After calling trans->RestartWithAuth(), this is the request we should - // be issuing -- the final header line contains the credentials. - MockWrite("GET / HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Connection: keep-alive\r\n" - "Authorization: Basic Zm9vOmJhcg==\r\n\r\n"), + // After calling trans->RestartWithAuth(), this is the request we should + // be issuing -- the final header line contains the credentials. + MockWrite( + "GET / HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Connection: keep-alive\r\n" + "Authorization: Basic Zm9vOmJhcg==\r\n\r\n"), }; // Respond with 5 kb of response body. @@ -2290,21 +2300,23 @@ TEST_P(HttpNetworkTransactionTest, BasicAuthKeepAliveImpatientServer) { HttpRequestInfo request; request.method = "GET"; - request.url = GURL("http://www.google.com/"); + request.url = GURL("http://www.example.org/"); request.load_flags = 0; scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps_)); MockWrite data_writes1[] = { - MockWrite("GET / HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Connection: keep-alive\r\n\r\n"), - // This simulates the seemingly successful write to a closed connection - // if the bug is not fixed. - MockWrite("GET / HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Connection: keep-alive\r\n" - "Authorization: Basic Zm9vOmJhcg==\r\n\r\n"), + MockWrite( + "GET / HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Connection: keep-alive\r\n\r\n"), + // This simulates the seemingly successful write to a closed connection + // if the bug is not fixed. + MockWrite( + "GET / HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Connection: keep-alive\r\n" + "Authorization: Basic Zm9vOmJhcg==\r\n\r\n"), }; MockRead data_reads1[] = { @@ -2321,10 +2333,11 @@ // After calling trans->RestartWithAuth(), this is the request we should // be issuing -- the final header line contains the credentials. MockWrite data_writes2[] = { - MockWrite("GET / HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Connection: keep-alive\r\n" - "Authorization: Basic Zm9vOmJhcg==\r\n\r\n"), + MockWrite( + "GET / HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Connection: keep-alive\r\n" + "Authorization: Basic Zm9vOmJhcg==\r\n\r\n"), }; // Lastly, the server responds with the actual content. @@ -2376,7 +2389,7 @@ TEST_P(HttpNetworkTransactionTest, BasicAuthProxyNoKeepAliveHttp10) { HttpRequestInfo request; request.method = "GET"; - request.url = GURL("https://www.google.com/"); + request.url = GURL("https://www.example.org/"); // when the no authentication data flag is set. request.load_flags = net::LOAD_DO_NOT_SEND_AUTH_DATA; @@ -2390,21 +2403,21 @@ // Since we have proxy, should try to establish tunnel. MockWrite data_writes1[] = { MockWrite( - "CONNECT www.google.com:443 HTTP/1.1\r\n" - "Host: www.google.com\r\n" + "CONNECT www.example.org:443 HTTP/1.1\r\n" + "Host: www.example.org\r\n" "Proxy-Connection: keep-alive\r\n\r\n"), // After calling trans->RestartWithAuth(), this is the request we should // be issuing -- the final header line contains the credentials. MockWrite( - "CONNECT www.google.com:443 HTTP/1.1\r\n" - "Host: www.google.com\r\n" + "CONNECT www.example.org:443 HTTP/1.1\r\n" + "Host: www.example.org\r\n" "Proxy-Connection: keep-alive\r\n" "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n\r\n"), MockWrite( "GET / HTTP/1.1\r\n" - "Host: www.google.com\r\n" + "Host: www.example.org\r\n" "Connection: keep-alive\r\n\r\n"), }; @@ -2494,7 +2507,7 @@ TEST_P(HttpNetworkTransactionTest, BasicAuthProxyNoKeepAliveHttp11) { HttpRequestInfo request; request.method = "GET"; - request.url = GURL("https://www.google.com/"); + request.url = GURL("https://www.example.org/"); // when the no authentication data flag is set. request.load_flags = net::LOAD_DO_NOT_SEND_AUTH_DATA; @@ -2507,20 +2520,23 @@ // Since we have proxy, should try to establish tunnel. MockWrite data_writes1[] = { - MockWrite("CONNECT www.google.com:443 HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Proxy-Connection: keep-alive\r\n\r\n"), + MockWrite( + "CONNECT www.example.org:443 HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Proxy-Connection: keep-alive\r\n\r\n"), - // After calling trans->RestartWithAuth(), this is the request we should - // be issuing -- the final header line contains the credentials. - MockWrite("CONNECT www.google.com:443 HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Proxy-Connection: keep-alive\r\n" - "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n\r\n"), + // After calling trans->RestartWithAuth(), this is the request we should + // be issuing -- the final header line contains the credentials. + MockWrite( + "CONNECT www.example.org:443 HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Proxy-Connection: keep-alive\r\n" + "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n\r\n"), - MockWrite("GET / HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Connection: keep-alive\r\n\r\n"), + MockWrite( + "GET / HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Connection: keep-alive\r\n\r\n"), }; // The proxy responds to the connect with a 407, using a persistent @@ -2611,7 +2627,7 @@ TEST_P(HttpNetworkTransactionTest, BasicAuthProxyKeepAliveHttp10) { HttpRequestInfo request; request.method = "GET"; - request.url = GURL("https://www.google.com/"); + request.url = GURL("https://www.example.org/"); // Ensure that proxy authentication is attempted even // when the no authentication data flag is set. request.load_flags = net::LOAD_DO_NOT_SEND_AUTH_DATA; @@ -2628,15 +2644,15 @@ // Since we have proxy, should try to establish tunnel. MockWrite data_writes1[] = { MockWrite( - "CONNECT www.google.com:443 HTTP/1.1\r\n" - "Host: www.google.com\r\n" + "CONNECT www.example.org:443 HTTP/1.1\r\n" + "Host: www.example.org\r\n" "Proxy-Connection: keep-alive\r\n\r\n"), // After calling trans->RestartWithAuth(), this is the request we should // be issuing -- the final header line contains the credentials. MockWrite( - "CONNECT www.google.com:443 HTTP/1.1\r\n" - "Host: www.google.com\r\n" + "CONNECT www.example.org:443 HTTP/1.1\r\n" + "Host: www.example.org\r\n" "Proxy-Connection: keep-alive\r\n" "Proxy-Authorization: Basic Zm9vOmJheg==\r\n\r\n"), }; @@ -2718,7 +2734,7 @@ TEST_P(HttpNetworkTransactionTest, BasicAuthProxyKeepAliveHttp11) { HttpRequestInfo request; request.method = "GET"; - request.url = GURL("https://www.google.com/"); + request.url = GURL("https://www.example.org/"); // Ensure that proxy authentication is attempted even // when the no authentication data flag is set. request.load_flags = net::LOAD_DO_NOT_SEND_AUTH_DATA; @@ -2734,16 +2750,18 @@ // Since we have proxy, should try to establish tunnel. MockWrite data_writes1[] = { - MockWrite("CONNECT www.google.com:443 HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Proxy-Connection: keep-alive\r\n\r\n"), + MockWrite( + "CONNECT www.example.org:443 HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Proxy-Connection: keep-alive\r\n\r\n"), - // After calling trans->RestartWithAuth(), this is the request we should - // be issuing -- the final header line contains the credentials. - MockWrite("CONNECT www.google.com:443 HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Proxy-Connection: keep-alive\r\n" - "Proxy-Authorization: Basic Zm9vOmJheg==\r\n\r\n"), + // After calling trans->RestartWithAuth(), this is the request we should + // be issuing -- the final header line contains the credentials. + MockWrite( + "CONNECT www.example.org:443 HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Proxy-Connection: keep-alive\r\n" + "Proxy-Authorization: Basic Zm9vOmJheg==\r\n\r\n"), }; // The proxy responds to the connect with a 407, using a persistent @@ -2822,7 +2840,7 @@ TEST_P(HttpNetworkTransactionTest, BasicAuthProxyCancelTunnel) { HttpRequestInfo request; request.method = "GET"; - request.url = GURL("https://www.google.com/"); + request.url = GURL("https://www.example.org/"); request.load_flags = 0; // Configure against proxy server "myproxy:70". @@ -2835,9 +2853,10 @@ // Since we have proxy, should try to establish tunnel. MockWrite data_writes[] = { - MockWrite("CONNECT www.google.com:443 HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Proxy-Connection: keep-alive\r\n\r\n"), + MockWrite( + "CONNECT www.example.org:443 HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Proxy-Connection: keep-alive\r\n\r\n"), }; // The proxy responds to the connect with a 407. @@ -2881,7 +2900,7 @@ TEST_P(HttpNetworkTransactionTest, SanitizeProxyAuthHeaders) { HttpRequestInfo request; request.method = "GET"; - request.url = GURL("https://www.google.com/"); + request.url = GURL("https://www.example.org/"); request.load_flags = 0; // Configure against proxy server "myproxy:70". @@ -2895,8 +2914,8 @@ // Since we have proxy, should try to establish tunnel. MockWrite data_writes[] = { MockWrite( - "CONNECT www.google.com:443 HTTP/1.1\r\n" - "Host: www.google.com\r\n" + "CONNECT www.example.org:443 HTTP/1.1\r\n" + "Host: www.example.org\r\n" "Proxy-Connection: keep-alive\r\n\r\n"), }; @@ -2944,7 +2963,7 @@ TEST_P(HttpNetworkTransactionTest, UnexpectedProxyAuth) { HttpRequestInfo request; request.method = "GET"; - request.url = GURL("http://www.google.com/"); + request.url = GURL("http://www.example.org/"); request.load_flags = 0; // We are using a DIRECT connection (i.e. no proxy) for this session. @@ -2953,9 +2972,10 @@ new HttpNetworkTransaction(DEFAULT_PRIORITY, session.get())); MockWrite data_writes1[] = { - MockWrite("GET / HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Connection: keep-alive\r\n\r\n"), + MockWrite( + "GET / HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Connection: keep-alive\r\n\r\n"), }; MockRead data_reads1[] = { @@ -2990,7 +3010,7 @@ HttpsServerRequestsProxyAuthThroughProxy) { HttpRequestInfo request; request.method = "GET"; - request.url = GURL("https://www.google.com/"); + request.url = GURL("https://www.example.org/"); session_deps_.proxy_service.reset(ProxyService::CreateFixed("myproxy:70")); BoundTestNetLog log; @@ -2999,13 +3019,15 @@ // Since we have proxy, should try to establish tunnel. MockWrite data_writes1[] = { - MockWrite("CONNECT www.google.com:443 HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Proxy-Connection: keep-alive\r\n\r\n"), + MockWrite( + "CONNECT www.example.org:443 HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Proxy-Connection: keep-alive\r\n\r\n"), - MockWrite("GET / HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Connection: keep-alive\r\n\r\n"), + MockWrite( + "GET / HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Connection: keep-alive\r\n\r\n"), }; MockRead data_reads1[] = { @@ -3048,11 +3070,11 @@ TEST_P(HttpNetworkTransactionTest, HttpProxyLoadTimingNoPacTwoRequests) { HttpRequestInfo request1; request1.method = "GET"; - request1.url = GURL("https://www.google.com/1"); + request1.url = GURL("https://www.example.org/1"); HttpRequestInfo request2; request2.method = "GET"; - request2.url = GURL("https://www.google.com/2"); + request2.url = GURL("https://www.example.org/2"); // Configure against proxy server "myproxy:70". session_deps_.proxy_service.reset( @@ -3063,17 +3085,20 @@ // Since we have proxy, should try to establish tunnel. MockWrite data_writes1[] = { - MockWrite("CONNECT www.google.com:443 HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Proxy-Connection: keep-alive\r\n\r\n"), + MockWrite( + "CONNECT www.example.org:443 HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Proxy-Connection: keep-alive\r\n\r\n"), - MockWrite("GET /1 HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Connection: keep-alive\r\n\r\n"), + MockWrite( + "GET /1 HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Connection: keep-alive\r\n\r\n"), - MockWrite("GET /2 HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Connection: keep-alive\r\n\r\n"), + MockWrite( + "GET /2 HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Connection: keep-alive\r\n\r\n"), }; // The proxy responds to the connect with a 407, using a persistent @@ -3146,11 +3171,11 @@ TEST_P(HttpNetworkTransactionTest, HttpProxyLoadTimingWithPacTwoRequests) { HttpRequestInfo request1; request1.method = "GET"; - request1.url = GURL("https://www.google.com/1"); + request1.url = GURL("https://www.example.org/1"); HttpRequestInfo request2; request2.method = "GET"; - request2.url = GURL("https://www.google.com/2"); + request2.url = GURL("https://www.example.org/2"); // Configure against proxy server "myproxy:70". session_deps_.proxy_service.reset( @@ -3161,17 +3186,20 @@ // Since we have proxy, should try to establish tunnel. MockWrite data_writes1[] = { - MockWrite("CONNECT www.google.com:443 HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Proxy-Connection: keep-alive\r\n\r\n"), + MockWrite( + "CONNECT www.example.org:443 HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Proxy-Connection: keep-alive\r\n\r\n"), - MockWrite("GET /1 HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Connection: keep-alive\r\n\r\n"), + MockWrite( + "GET /1 HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Connection: keep-alive\r\n\r\n"), - MockWrite("GET /2 HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Connection: keep-alive\r\n\r\n"), + MockWrite( + "GET /2 HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Connection: keep-alive\r\n\r\n"), }; // The proxy responds to the connect with a 407, using a persistent @@ -3245,7 +3273,7 @@ TEST_P(HttpNetworkTransactionTest, HttpsProxyGet) { HttpRequestInfo request; request.method = "GET"; - request.url = GURL("http://www.google.com/"); + request.url = GURL("http://www.example.org/"); // Configure against https proxy server "proxy:70". session_deps_.proxy_service.reset(ProxyService::CreateFixed( @@ -3256,9 +3284,10 @@ // Since we have proxy, should use full url MockWrite data_writes1[] = { - MockWrite("GET http://www.google.com/ HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Proxy-Connection: keep-alive\r\n\r\n"), + MockWrite( + "GET http://www.example.org/ HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Proxy-Connection: keep-alive\r\n\r\n"), }; MockRead data_reads1[] = { @@ -3306,7 +3335,7 @@ TEST_P(HttpNetworkTransactionTest, HttpsProxySpdyGet) { HttpRequestInfo request; request.method = "GET"; - request.url = GURL("http://www.google.com/"); + request.url = GURL("http://www.example.org/"); request.load_flags = 0; // Configure against https proxy server "proxy:70". @@ -3316,7 +3345,7 @@ session_deps_.net_log = log.bound().net_log(); scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps_)); - // fetch http://www.google.com/ via SPDY + // fetch http://www.example.org/ via SPDY scoped_ptr<SpdyFrame> req( spdy_util_.ConstructSpdyGet(NULL, 0, false, 1, LOWEST, false)); MockWrite spdy_writes[] = { CreateMockWrite(*req) }; @@ -3371,7 +3400,7 @@ TEST_P(HttpNetworkTransactionTest, HttpsProxySpdyGetWithSessionRace) { HttpRequestInfo request; request.method = "GET"; - request.url = GURL("http://www.google.com/"); + request.url = GURL("http://www.example.org/"); request.load_flags = 0; // Configure SPDY proxy server "proxy:70". @@ -3381,7 +3410,7 @@ session_deps_.net_log = log.bound().net_log(); scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps_)); - // Fetch http://www.google.com/ through the SPDY proxy. + // Fetch http://www.example.org/ through the SPDY proxy. scoped_ptr<SpdyFrame> req( spdy_util_.ConstructSpdyGet(NULL, 0, false, 1, LOWEST, false)); MockWrite spdy_writes[] = {CreateMockWrite(*req)}; @@ -3445,7 +3474,7 @@ TEST_P(HttpNetworkTransactionTest, HttpsProxySpdyGetWithProxyAuth) { HttpRequestInfo request; request.method = "GET"; - request.url = GURL("http://www.google.com/"); + request.url = GURL("http://www.example.org/"); request.load_flags = 0; // Configure against https proxy server "myproxy:70". @@ -3548,7 +3577,7 @@ TEST_P(HttpNetworkTransactionTest, HttpsProxySpdyConnectHttps) { HttpRequestInfo request; request.method = "GET"; - request.url = GURL("https://www.google.com/"); + request.url = GURL("https://www.example.org/"); request.load_flags = 0; // Configure against https proxy server "proxy:70". @@ -3561,14 +3590,15 @@ scoped_ptr<HttpTransaction> trans( new HttpNetworkTransaction(DEFAULT_PRIORITY, session.get())); - // CONNECT to www.google.com:443 via SPDY + // CONNECT to www.example.org:443 via SPDY scoped_ptr<SpdyFrame> connect(spdy_util_.ConstructSpdyConnect( - NULL, 0, 1, LOWEST, HostPortPair("www.google.com", 443))); - // fetch https://www.google.com/ via HTTP + NULL, 0, 1, LOWEST, HostPortPair("www.example.org", 443))); + // fetch https://www.example.org/ via HTTP - const char get[] = "GET / HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Connection: keep-alive\r\n\r\n"; + const char get[] = + "GET / HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Connection: keep-alive\r\n\r\n"; scoped_ptr<SpdyFrame> wrapped_get( spdy_util_.ConstructSpdyBodyFrame(1, get, strlen(get), false)); scoped_ptr<SpdyFrame> conn_resp( @@ -3635,7 +3665,7 @@ TEST_P(HttpNetworkTransactionTest, HttpsProxySpdyConnectSpdy) { HttpRequestInfo request; request.method = "GET"; - request.url = GURL("https://www.google.com/"); + request.url = GURL("https://www.example.org/"); request.load_flags = 0; // Configure against https proxy server "proxy:70". @@ -3648,11 +3678,11 @@ scoped_ptr<HttpTransaction> trans( new HttpNetworkTransaction(DEFAULT_PRIORITY, session.get())); - // CONNECT to www.google.com:443 via SPDY + // CONNECT to www.example.org:443 via SPDY scoped_ptr<SpdyFrame> connect(spdy_util_.ConstructSpdyConnect( - NULL, 0, 1, LOWEST, HostPortPair("www.google.com", 443))); - // fetch https://www.google.com/ via SPDY - const char kMyUrl[] = "https://www.google.com/"; + NULL, 0, 1, LOWEST, HostPortPair("www.example.org", 443))); + // fetch https://www.example.org/ via SPDY + const char kMyUrl[] = "https://www.example.org/"; scoped_ptr<SpdyFrame> get( spdy_util_.ConstructSpdyGet(kMyUrl, false, 1, LOWEST)); scoped_ptr<SpdyFrame> wrapped_get( @@ -3724,7 +3754,7 @@ TEST_P(HttpNetworkTransactionTest, HttpsProxySpdyConnectFailure) { HttpRequestInfo request; request.method = "GET"; - request.url = GURL("https://www.google.com/"); + request.url = GURL("https://www.example.org/"); request.load_flags = 0; // Configure against https proxy server "proxy:70". @@ -3737,9 +3767,9 @@ scoped_ptr<HttpTransaction> trans( new HttpNetworkTransaction(DEFAULT_PRIORITY, session.get())); - // CONNECT to www.google.com:443 via SPDY + // CONNECT to www.example.org:443 via SPDY scoped_ptr<SpdyFrame> connect(spdy_util_.ConstructSpdyConnect( - NULL, 0, 1, LOWEST, HostPortPair("www.google.com", 443))); + NULL, 0, 1, LOWEST, HostPortPair("www.example.org", 443))); scoped_ptr<SpdyFrame> get( spdy_util_.ConstructSpdyRstStream(1, RST_STREAM_CANCEL)); @@ -3792,23 +3822,24 @@ HttpRequestInfo request1; request1.method = "GET"; - request1.url = GURL("https://www.google.com/"); + request1.url = GURL("https://www.example.org/"); request1.load_flags = 0; HttpRequestInfo request2; request2.method = "GET"; - request2.url = GURL("https://news.google.com/"); + request2.url = GURL("https://mail.example.org/"); request2.load_flags = 0; - // CONNECT to www.google.com:443 via SPDY. + // CONNECT to www.example.org:443 via SPDY. scoped_ptr<SpdyFrame> connect1(spdy_util_.ConstructSpdyConnect( - NULL, 0, 1, LOWEST, HostPortPair("www.google.com", 443))); + NULL, 0, 1, LOWEST, HostPortPair("www.example.org", 443))); scoped_ptr<SpdyFrame> conn_resp1( spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1)); - // Fetch https://www.google.com/ via HTTP. - const char get1[] = "GET / HTTP/1.1\r\n" - "Host: www.google.com\r\n" + // Fetch https://www.example.org/ via HTTP. + const char get1[] = + "GET / HTTP/1.1\r\n" + "Host: www.example.org\r\n" "Connection: keep-alive\r\n\r\n"; scoped_ptr<SpdyFrame> wrapped_get1( spdy_util_.ConstructSpdyBodyFrame(1, get1, strlen(get1), false)); @@ -3821,11 +3852,11 @@ scoped_ptr<SpdyFrame> window_update( spdy_util_.ConstructSpdyWindowUpdate(1, wrapped_get_resp1->size())); - // CONNECT to news.google.com:443 via SPDY. + // CONNECT to mail.example.org:443 via SPDY. SpdyHeaderBlock connect2_block; connect2_block[spdy_util_.GetMethodKey()] = "CONNECT"; - connect2_block[spdy_util_.GetPathKey()] = "news.google.com:443"; - connect2_block[spdy_util_.GetHostKey()] = "news.google.com"; + connect2_block[spdy_util_.GetPathKey()] = "mail.example.org:443"; + connect2_block[spdy_util_.GetHostKey()] = "mail.example.org"; spdy_util_.MaybeAddVersionHeader(&connect2_block); scoped_ptr<SpdyFrame> connect2( spdy_util_.ConstructSpdySyn(3, connect2_block, LOWEST, false, false)); @@ -3833,9 +3864,10 @@ scoped_ptr<SpdyFrame> conn_resp2( spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 3)); - // Fetch https://news.google.com/ via HTTP. - const char get2[] = "GET / HTTP/1.1\r\n" - "Host: news.google.com\r\n" + // Fetch https://mail.example.org/ via HTTP. + const char get2[] = + "GET / HTTP/1.1\r\n" + "Host: mail.example.org\r\n" "Connection: keep-alive\r\n\r\n"; scoped_ptr<SpdyFrame> wrapped_get2( spdy_util_.ConstructSpdyBodyFrame(3, get2, strlen(get2), false)); @@ -3942,23 +3974,24 @@ HttpRequestInfo request1; request1.method = "GET"; - request1.url = GURL("https://www.google.com/"); + request1.url = GURL("https://www.example.org/"); request1.load_flags = 0; HttpRequestInfo request2; request2.method = "GET"; - request2.url = GURL("https://www.google.com/2"); + request2.url = GURL("https://www.example.org/2"); request2.load_flags = 0; - // CONNECT to www.google.com:443 via SPDY. + // CONNECT to www.example.org:443 via SPDY. scoped_ptr<SpdyFrame> connect1(spdy_util_.ConstructSpdyConnect( - NULL, 0, 1, LOWEST, HostPortPair("www.google.com", 443))); + NULL, 0, 1, LOWEST, HostPortPair("www.example.org", 443))); scoped_ptr<SpdyFrame> conn_resp1( spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1)); - // Fetch https://www.google.com/ via HTTP. - const char get1[] = "GET / HTTP/1.1\r\n" - "Host: www.google.com\r\n" + // Fetch https://www.example.org/ via HTTP. + const char get1[] = + "GET / HTTP/1.1\r\n" + "Host: www.example.org\r\n" "Connection: keep-alive\r\n\r\n"; scoped_ptr<SpdyFrame> wrapped_get1( spdy_util_.ConstructSpdyBodyFrame(1, get1, strlen(get1), false)); @@ -3971,9 +4004,10 @@ scoped_ptr<SpdyFrame> window_update( spdy_util_.ConstructSpdyWindowUpdate(1, wrapped_get_resp1->size())); - // Fetch https://www.google.com/2 via HTTP. - const char get2[] = "GET /2 HTTP/1.1\r\n" - "Host: www.google.com\r\n" + // Fetch https://www.example.org/2 via HTTP. + const char get2[] = + "GET /2 HTTP/1.1\r\n" + "Host: www.example.org\r\n" "Connection: keep-alive\r\n\r\n"; scoped_ptr<SpdyFrame> wrapped_get2( spdy_util_.ConstructSpdyBodyFrame(1, get2, strlen(get2), false)); @@ -4073,17 +4107,17 @@ HttpRequestInfo request1; request1.method = "GET"; - request1.url = GURL("http://www.google.com/"); + request1.url = GURL("http://www.example.org/"); request1.load_flags = 0; HttpRequestInfo request2; request2.method = "GET"; - request2.url = GURL("http://news.google.com/"); + request2.url = GURL("http://mail.example.org/"); request2.load_flags = 0; - // http://www.google.com/ + // http://www.example.org/ scoped_ptr<SpdyHeaderBlock> headers( - spdy_util_.ConstructGetHeaderBlockForProxy("http://www.google.com/")); + spdy_util_.ConstructGetHeaderBlockForProxy("http://www.example.org/")); scoped_ptr<SpdyFrame> get1( spdy_util_.ConstructSpdySyn(1, *headers, LOWEST, false, true)); scoped_ptr<SpdyFrame> get_resp1( @@ -4091,9 +4125,9 @@ scoped_ptr<SpdyFrame> body1( spdy_util_.ConstructSpdyBodyFrame(1, "1", 1, true)); - // http://news.google.com/ + // http://mail.example.org/ scoped_ptr<SpdyHeaderBlock> headers2( - spdy_util_.ConstructGetHeaderBlockForProxy("http://news.google.com/")); + spdy_util_.ConstructGetHeaderBlockForProxy("http://mail.example.org/")); scoped_ptr<SpdyFrame> get2( spdy_util_.ConstructSpdySyn(3, *headers2, LOWEST, false, true)); scoped_ptr<SpdyFrame> get_resp2( @@ -4177,7 +4211,7 @@ TEST_P(HttpNetworkTransactionTest, HttpsProxyAuthRetry) { HttpRequestInfo request; request.method = "GET"; - request.url = GURL("http://www.google.com/"); + request.url = GURL("http://www.example.org/"); // when the no authentication data flag is set. request.load_flags = net::LOAD_DO_NOT_SEND_AUTH_DATA; @@ -4190,16 +4224,18 @@ // Since we have proxy, should use full url MockWrite data_writes1[] = { - MockWrite("GET http://www.google.com/ HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Proxy-Connection: keep-alive\r\n\r\n"), + MockWrite( + "GET http://www.example.org/ HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Proxy-Connection: keep-alive\r\n\r\n"), - // After calling trans->RestartWithAuth(), this is the request we should - // be issuing -- the final header line contains the credentials. - MockWrite("GET http://www.google.com/ HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Proxy-Connection: keep-alive\r\n" - "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n\r\n"), + // After calling trans->RestartWithAuth(), this is the request we should + // be issuing -- the final header line contains the credentials. + MockWrite( + "GET http://www.example.org/ HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Proxy-Connection: keep-alive\r\n" + "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n\r\n"), }; // The proxy responds to the GET with a 407, using a persistent @@ -4276,7 +4312,7 @@ const MockRead& status, int expected_status) { HttpRequestInfo request; request.method = "GET"; - request.url = GURL("https://www.google.com/"); + request.url = GURL("https://www.example.org/"); request.load_flags = 0; // Configure against proxy server "myproxy:70". @@ -4285,9 +4321,10 @@ // Since we have proxy, should try to establish tunnel. MockWrite data_writes[] = { - MockWrite("CONNECT www.google.com:443 HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Proxy-Connection: keep-alive\r\n\r\n"), + MockWrite( + "CONNECT www.example.org:443 HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Proxy-Connection: keep-alive\r\n\r\n"), }; MockRead data_reads[] = { @@ -4493,7 +4530,7 @@ TEST_P(HttpNetworkTransactionTest, BasicAuthProxyThenServer) { HttpRequestInfo request; request.method = "GET"; - request.url = GURL("http://www.google.com/"); + request.url = GURL("http://www.example.org/"); request.load_flags = 0; // Configure against proxy server "myproxy:70". @@ -4504,9 +4541,10 @@ new HttpNetworkTransaction(DEFAULT_PRIORITY, session.get())); MockWrite data_writes1[] = { - MockWrite("GET http://www.google.com/ HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Proxy-Connection: keep-alive\r\n\r\n"), + MockWrite( + "GET http://www.example.org/ HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Proxy-Connection: keep-alive\r\n\r\n"), }; MockRead data_reads1[] = { @@ -4526,10 +4564,11 @@ // request we should be issuing -- the final header line contains the // proxy's credentials. MockWrite data_writes2[] = { - MockWrite("GET http://www.google.com/ HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Proxy-Connection: keep-alive\r\n" - "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n\r\n"), + MockWrite( + "GET http://www.example.org/ HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Proxy-Connection: keep-alive\r\n" + "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n\r\n"), }; // Now the proxy server lets the request pass through to origin server. @@ -4547,11 +4586,12 @@ // After calling trans->RestartWithAuth() the second time, we should send // the credentials for both the proxy and origin server. MockWrite data_writes3[] = { - MockWrite("GET http://www.google.com/ HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Proxy-Connection: keep-alive\r\n" - "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n" - "Authorization: Basic Zm9vMjpiYXIy\r\n\r\n"), + MockWrite( + "GET http://www.example.org/ HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Proxy-Connection: keep-alive\r\n" + "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n" + "Authorization: Basic Zm9vMjpiYXIy\r\n\r\n"), }; // Lastly we get the desired content. @@ -4957,7 +4997,7 @@ TEST_P(HttpNetworkTransactionTest, LargeHeadersNoBody) { HttpRequestInfo request; request.method = "GET"; - request.url = GURL("http://www.google.com/"); + request.url = GURL("http://www.example.org/"); request.load_flags = 0; scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps_)); @@ -4996,7 +5036,7 @@ DontRecycleTransportSocketForSSLTunnel) { HttpRequestInfo request; request.method = "GET"; - request.url = GURL("https://www.google.com/"); + request.url = GURL("https://www.example.org/"); request.load_flags = 0; // Configure against proxy server "myproxy:70". @@ -5009,9 +5049,10 @@ // Since we have proxy, should try to establish tunnel. MockWrite data_writes1[] = { - MockWrite("CONNECT www.google.com:443 HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Proxy-Connection: keep-alive\r\n\r\n"), + MockWrite( + "CONNECT www.example.org:443 HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Proxy-Connection: keep-alive\r\n\r\n"), }; // The proxy responds to the connect with a 404, using a persistent @@ -5055,7 +5096,7 @@ TEST_P(HttpNetworkTransactionTest, RecycleSocket) { HttpRequestInfo request; request.method = "GET"; - request.url = GURL("http://www.google.com/"); + request.url = GURL("http://www.example.org/"); request.load_flags = 0; scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps_)); @@ -5111,13 +5152,14 @@ TEST_P(HttpNetworkTransactionTest, RecycleSSLSocket) { HttpRequestInfo request; request.method = "GET"; - request.url = GURL("https://www.google.com/"); + request.url = GURL("https://www.example.org/"); request.load_flags = 0; MockWrite data_writes[] = { - MockWrite("GET / HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Connection: keep-alive\r\n\r\n"), + MockWrite( + "GET / HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Connection: keep-alive\r\n\r\n"), }; MockRead data_reads[] = { @@ -5170,16 +5212,18 @@ TEST_P(HttpNetworkTransactionTest, RecycleDeadSSLSocket) { HttpRequestInfo request; request.method = "GET"; - request.url = GURL("https://www.google.com/"); + request.url = GURL("https://www.example.org/"); request.load_flags = 0; MockWrite data_writes[] = { - MockWrite("GET / HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Connection: keep-alive\r\n\r\n"), - MockWrite("GET / HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Connection: keep-alive\r\n\r\n"), + MockWrite( + "GET / HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Connection: keep-alive\r\n\r\n"), + MockWrite( + "GET / HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Connection: keep-alive\r\n\r\n"), }; MockRead data_reads[] = { @@ -5265,10 +5309,11 @@ TEST_P(HttpNetworkTransactionTest, RecycleSocketAfterZeroContentLength) { HttpRequestInfo request; request.method = "GET"; - request.url = GURL("http://www.google.com/csi?v=3&s=web&action=&" - "tran=undefined&ei=mAXcSeegAo-SMurloeUN&" - "e=17259,18167,19592,19773,19981,20133,20173,20233&" - "rt=prt.2642,ol.2649,xjs.2951"); + request.url = GURL( + "http://www.example.org/csi?v=3&s=web&action=&" + "tran=undefined&ei=mAXcSeegAo-SMurloeUN&" + "e=17259,18167,19592,19773,19981,20133,20173,20233&" + "rt=prt.2642,ol.2649,xjs.2951"); request.load_flags = 0; scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps_)); @@ -5412,7 +5457,7 @@ TEST_P(HttpNetworkTransactionTest, AuthIdentityInURL) { HttpRequestInfo request; request.method = "GET"; - request.url = GURL("http://foo:b@r@www.google.com/"); + request.url = GURL("http://foo:b@r@www.example.org/"); request.load_flags = LOAD_NORMAL; scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps_)); @@ -5424,9 +5469,10 @@ EXPECT_EQ("b%40r", request.url.password()); MockWrite data_writes1[] = { - MockWrite("GET / HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Connection: keep-alive\r\n\r\n"), + MockWrite( + "GET / HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Connection: keep-alive\r\n\r\n"), }; MockRead data_reads1[] = { @@ -5439,10 +5485,11 @@ // After the challenge above, the transaction will be restarted using the // identity from the url (foo, b@r) to answer the challenge. MockWrite data_writes2[] = { - MockWrite("GET / HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Connection: keep-alive\r\n" - "Authorization: Basic Zm9vOmJAcg==\r\n\r\n"), + MockWrite( + "GET / HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Connection: keep-alive\r\n" + "Authorization: Basic Zm9vOmJAcg==\r\n\r\n"), }; MockRead data_reads2[] = { @@ -5492,7 +5539,7 @@ request.method = "GET"; // Note: the URL has a username:password in it. The password "baz" is // wrong (should be "bar"). - request.url = GURL("http://foo:baz@www.google.com/"); + request.url = GURL("http://foo:baz@www.example.org/"); request.load_flags = LOAD_NORMAL; @@ -5501,9 +5548,10 @@ new HttpNetworkTransaction(DEFAULT_PRIORITY, session.get())); MockWrite data_writes1[] = { - MockWrite("GET / HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Connection: keep-alive\r\n\r\n"), + MockWrite( + "GET / HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Connection: keep-alive\r\n\r\n"), }; MockRead data_reads1[] = { @@ -5516,10 +5564,11 @@ // After the challenge above, the transaction will be restarted using the // identity from the url (foo, baz) to answer the challenge. MockWrite data_writes2[] = { - MockWrite("GET / HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Connection: keep-alive\r\n" - "Authorization: Basic Zm9vOmJheg==\r\n\r\n"), + MockWrite( + "GET / HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Connection: keep-alive\r\n" + "Authorization: Basic Zm9vOmJheg==\r\n\r\n"), }; MockRead data_reads2[] = { @@ -5532,10 +5581,11 @@ // After the challenge above, the transaction will be restarted using the // identity supplied by the user (foo, bar) to answer the challenge. MockWrite data_writes3[] = { - MockWrite("GET / HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Connection: keep-alive\r\n" - "Authorization: Basic Zm9vOmJhcg==\r\n\r\n"), + MockWrite( + "GET / HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Connection: keep-alive\r\n" + "Authorization: Basic Zm9vOmJhcg==\r\n\r\n"), }; MockRead data_reads3[] = { @@ -5601,7 +5651,7 @@ TEST_P(HttpNetworkTransactionTest, AuthIdentityInURLSuppressed) { HttpRequestInfo request; request.method = "GET"; - request.url = GURL("http://foo:bar@www.google.com/"); + request.url = GURL("http://foo:bar@www.example.org/"); request.load_flags = LOAD_DO_NOT_USE_EMBEDDED_IDENTITY; scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps_)); @@ -5609,9 +5659,10 @@ new HttpNetworkTransaction(DEFAULT_PRIORITY, session.get())); MockWrite data_writes1[] = { - MockWrite("GET / HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Connection: keep-alive\r\n\r\n"), + MockWrite( + "GET / HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Connection: keep-alive\r\n\r\n"), }; MockRead data_reads1[] = { @@ -5625,10 +5676,11 @@ // identity supplied by the user, not the one in the URL, to answer the // challenge. MockWrite data_writes3[] = { - MockWrite("GET / HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Connection: keep-alive\r\n" - "Authorization: Basic Zm9vOmJhcg==\r\n\r\n"), + MockWrite( + "GET / HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Connection: keep-alive\r\n" + "Authorization: Basic Zm9vOmJhcg==\r\n\r\n"), }; MockRead data_reads3[] = { @@ -5682,16 +5734,17 @@ { HttpRequestInfo request; request.method = "GET"; - request.url = GURL("http://www.google.com/x/y/z"); + request.url = GURL("http://www.example.org/x/y/z"); request.load_flags = 0; scoped_ptr<HttpTransaction> trans( new HttpNetworkTransaction(DEFAULT_PRIORITY, session.get())); MockWrite data_writes1[] = { - MockWrite("GET /x/y/z HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Connection: keep-alive\r\n\r\n"), + MockWrite( + "GET /x/y/z HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Connection: keep-alive\r\n\r\n"), }; MockRead data_reads1[] = { @@ -5703,10 +5756,11 @@ // Resend with authorization (username=foo, password=bar) MockWrite data_writes2[] = { - MockWrite("GET /x/y/z HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Connection: keep-alive\r\n" - "Authorization: Basic Zm9vOmJhcg==\r\n\r\n"), + MockWrite( + "GET /x/y/z HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Connection: keep-alive\r\n" + "Authorization: Basic Zm9vOmJhcg==\r\n\r\n"), }; // Sever accepts the authorization. @@ -5758,18 +5812,19 @@ request.method = "GET"; // Note that Transaction 1 was at /x/y/z, so this is in the same // protection space as MyRealm1. - request.url = GURL("http://www.google.com/x/y/a/b"); + request.url = GURL("http://www.example.org/x/y/a/b"); request.load_flags = 0; scoped_ptr<HttpTransaction> trans( new HttpNetworkTransaction(DEFAULT_PRIORITY, session.get())); MockWrite data_writes1[] = { - MockWrite("GET /x/y/a/b HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Connection: keep-alive\r\n" - // Send preemptive authorization for MyRealm1 - "Authorization: Basic Zm9vOmJhcg==\r\n\r\n"), + MockWrite( + "GET /x/y/a/b HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Connection: keep-alive\r\n" + // Send preemptive authorization for MyRealm1 + "Authorization: Basic Zm9vOmJhcg==\r\n\r\n"), }; // The server didn't like the preemptive authorization, and @@ -5783,10 +5838,11 @@ // Resend with authorization for MyRealm2 (username=foo2, password=bar2) MockWrite data_writes2[] = { - MockWrite("GET /x/y/a/b HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Connection: keep-alive\r\n" - "Authorization: Basic Zm9vMjpiYXIy\r\n\r\n"), + MockWrite( + "GET /x/y/a/b HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Connection: keep-alive\r\n" + "Authorization: Basic Zm9vMjpiYXIy\r\n\r\n"), }; // Sever accepts the authorization. @@ -5815,7 +5871,7 @@ ASSERT_TRUE(response != NULL); ASSERT_TRUE(response->auth_challenge.get()); EXPECT_FALSE(response->auth_challenge->is_proxy); - EXPECT_EQ("www.google.com:80", + EXPECT_EQ("www.example.org:80", response->auth_challenge->challenger.ToString()); EXPECT_EQ("MyRealm2", response->auth_challenge->realm); EXPECT_EQ("basic", response->auth_challenge->scheme); @@ -5842,19 +5898,20 @@ { HttpRequestInfo request; request.method = "GET"; - request.url = GURL("http://www.google.com/x/y/z2"); + request.url = GURL("http://www.example.org/x/y/z2"); request.load_flags = 0; scoped_ptr<HttpTransaction> trans( new HttpNetworkTransaction(DEFAULT_PRIORITY, session.get())); MockWrite data_writes1[] = { - MockWrite("GET /x/y/z2 HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Connection: keep-alive\r\n" - // The authorization for MyRealm1 gets sent preemptively - // (since the url is in the same protection space) - "Authorization: Basic Zm9vOmJhcg==\r\n\r\n"), + MockWrite( + "GET /x/y/z2 HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Connection: keep-alive\r\n" + // The authorization for MyRealm1 gets sent preemptively + // (since the url is in the same protection space) + "Authorization: Basic Zm9vOmJhcg==\r\n\r\n"), }; // Sever accepts the preemptive authorization @@ -5890,16 +5947,17 @@ { HttpRequestInfo request; request.method = "GET"; - request.url = GURL("http://www.google.com/x/1"); + request.url = GURL("http://www.example.org/x/1"); request.load_flags = 0; scoped_ptr<HttpTransaction> trans( new HttpNetworkTransaction(DEFAULT_PRIORITY, session.get())); MockWrite data_writes1[] = { - MockWrite("GET /x/1 HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Connection: keep-alive\r\n\r\n"), + MockWrite( + "GET /x/1 HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Connection: keep-alive\r\n\r\n"), }; MockRead data_reads1[] = { @@ -5911,10 +5969,11 @@ // Resend with authorization from MyRealm's cache. MockWrite data_writes2[] = { - MockWrite("GET /x/1 HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Connection: keep-alive\r\n" - "Authorization: Basic Zm9vOmJhcg==\r\n\r\n"), + MockWrite( + "GET /x/1 HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Connection: keep-alive\r\n" + "Authorization: Basic Zm9vOmJhcg==\r\n\r\n"), }; // Sever accepts the authorization. @@ -5960,16 +6019,17 @@ { HttpRequestInfo request; request.method = "GET"; - request.url = GURL("http://www.google.com/p/q/t"); + request.url = GURL("http://www.example.org/p/q/t"); request.load_flags = 0; scoped_ptr<HttpTransaction> trans( new HttpNetworkTransaction(DEFAULT_PRIORITY, session.get())); MockWrite data_writes1[] = { - MockWrite("GET /p/q/t HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Connection: keep-alive\r\n\r\n"), + MockWrite( + "GET /p/q/t HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Connection: keep-alive\r\n\r\n"), }; MockRead data_reads1[] = { @@ -5981,10 +6041,11 @@ // Resend with authorization from cache for MyRealm. MockWrite data_writes2[] = { - MockWrite("GET /p/q/t HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Connection: keep-alive\r\n" - "Authorization: Basic Zm9vOmJhcg==\r\n\r\n"), + MockWrite( + "GET /p/q/t HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Connection: keep-alive\r\n" + "Authorization: Basic Zm9vOmJhcg==\r\n\r\n"), }; // Sever rejects the authorization. @@ -5998,10 +6059,11 @@ // At this point we should prompt for new credentials for MyRealm. // Restart with username=foo3, password=foo4. MockWrite data_writes3[] = { - MockWrite("GET /p/q/t HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Connection: keep-alive\r\n" - "Authorization: Basic Zm9vMzpiYXIz\r\n\r\n"), + MockWrite( + "GET /p/q/t HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Connection: keep-alive\r\n" + "Authorization: Basic Zm9vMzpiYXIz\r\n\r\n"), }; // Sever accepts the authorization. @@ -6072,16 +6134,17 @@ { HttpRequestInfo request; request.method = "GET"; - request.url = GURL("http://www.google.com/x/y/z"); + request.url = GURL("http://www.example.org/x/y/z"); request.load_flags = 0; scoped_ptr<HttpTransaction> trans( new HttpNetworkTransaction(DEFAULT_PRIORITY, session.get())); MockWrite data_writes1[] = { - MockWrite("GET /x/y/z HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Connection: keep-alive\r\n\r\n"), + MockWrite( + "GET /x/y/z HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Connection: keep-alive\r\n\r\n"), }; MockRead data_reads1[] = { @@ -6093,13 +6156,14 @@ // Resend with authorization (username=foo, password=bar) MockWrite data_writes2[] = { - MockWrite("GET /x/y/z HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Connection: keep-alive\r\n" - "Authorization: Digest username=\"foo\", realm=\"digestive\", " - "nonce=\"OU812\", uri=\"/x/y/z\", algorithm=MD5, " - "response=\"03ffbcd30add722589c1de345d7a927f\", qop=auth, " - "nc=00000001, cnonce=\"0123456789abcdef\"\r\n\r\n"), + MockWrite( + "GET /x/y/z HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Connection: keep-alive\r\n" + "Authorization: Digest username=\"foo\", realm=\"digestive\", " + "nonce=\"OU812\", uri=\"/x/y/z\", algorithm=MD5, " + "response=\"03ffbcd30add722589c1de345d7a927f\", qop=auth, " + "nc=00000001, cnonce=\"0123456789abcdef\"\r\n\r\n"), }; // Sever accepts the authorization. @@ -6151,20 +6215,21 @@ request.method = "GET"; // Note that Transaction 1 was at /x/y/z, so this is in the same // protection space as digest. - request.url = GURL("http://www.google.com/x/y/a/b"); + request.url = GURL("http://www.example.org/x/y/a/b"); request.load_flags = 0; scoped_ptr<HttpTransaction> trans( new HttpNetworkTransaction(DEFAULT_PRIORITY, session.get())); MockWrite data_writes1[] = { - MockWrite("GET /x/y/a/b HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Connection: keep-alive\r\n" - "Authorization: Digest username=\"foo\", realm=\"digestive\", " - "nonce=\"OU812\", uri=\"/x/y/a/b\", algorithm=MD5, " - "response=\"d6f9a2c07d1c5df7b89379dca1269b35\", qop=auth, " - "nc=00000002, cnonce=\"0123456789abcdef\"\r\n\r\n"), + MockWrite( + "GET /x/y/a/b HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Connection: keep-alive\r\n" + "Authorization: Digest username=\"foo\", realm=\"digestive\", " + "nonce=\"OU812\", uri=\"/x/y/a/b\", algorithm=MD5, " + "response=\"d6f9a2c07d1c5df7b89379dca1269b35\", qop=auth, " + "nc=00000002, cnonce=\"0123456789abcdef\"\r\n\r\n"), }; // Sever accepts the authorization. @@ -6239,7 +6304,7 @@ TEST_P(HttpNetworkTransactionTest, HTTPSBadCertificate) { HttpRequestInfo request; request.method = "GET"; - request.url = GURL("https://www.google.com/"); + request.url = GURL("https://www.example.org/"); request.load_flags = 0; scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps_)); @@ -6247,9 +6312,10 @@ new HttpNetworkTransaction(DEFAULT_PRIORITY, session.get())); MockWrite data_writes[] = { - MockWrite("GET / HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Connection: keep-alive\r\n\r\n"), + MockWrite( + "GET / HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Connection: keep-alive\r\n\r\n"), }; MockRead data_reads[] = { @@ -6297,13 +6363,14 @@ HttpRequestInfo request; request.method = "GET"; - request.url = GURL("https://www.google.com/"); + request.url = GURL("https://www.example.org/"); request.load_flags = 0; MockWrite proxy_writes[] = { - MockWrite("CONNECT www.google.com:443 HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Proxy-Connection: keep-alive\r\n\r\n"), + MockWrite( + "CONNECT www.example.org:443 HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Proxy-Connection: keep-alive\r\n\r\n"), }; MockRead proxy_reads[] = { @@ -6312,12 +6379,14 @@ }; MockWrite data_writes[] = { - MockWrite("CONNECT www.google.com:443 HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Proxy-Connection: keep-alive\r\n\r\n"), - MockWrite("GET / HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Connection: keep-alive\r\n\r\n"), + MockWrite( + "CONNECT www.example.org:443 HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Proxy-Connection: keep-alive\r\n\r\n"), + MockWrite( + "GET / HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Connection: keep-alive\r\n\r\n"), }; MockRead data_reads[] = { @@ -6379,16 +6448,18 @@ HttpRequestInfo request; request.method = "GET"; - request.url = GURL("https://www.google.com/"); + request.url = GURL("https://www.example.org/"); request.load_flags = 0; MockWrite data_writes[] = { - MockWrite("CONNECT www.google.com:443 HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Proxy-Connection: keep-alive\r\n\r\n"), - MockWrite("GET / HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Connection: keep-alive\r\n\r\n"), + MockWrite( + "CONNECT www.example.org:443 HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Proxy-Connection: keep-alive\r\n\r\n"), + MockWrite( + "GET / HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Connection: keep-alive\r\n\r\n"), }; MockRead data_reads[] = { @@ -6443,13 +6514,14 @@ HttpRequestInfo request; request.method = "GET"; - request.url = GURL("https://www.google.com/"); + request.url = GURL("https://www.example.org/"); request.load_flags = 0; MockWrite data_writes[] = { - MockWrite("CONNECT www.google.com:443 HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Proxy-Connection: keep-alive\r\n\r\n"), + MockWrite( + "CONNECT www.example.org:443 HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Proxy-Connection: keep-alive\r\n\r\n"), }; MockRead data_reads[] = { @@ -6517,11 +6589,11 @@ HttpRequestInfo request; request.method = "GET"; - request.url = GURL("https://www.google.com/"); + request.url = GURL("https://www.example.org/"); request.load_flags = 0; scoped_ptr<SpdyFrame> conn(spdy_util_.ConstructSpdyConnect( - NULL, 0, 1, LOWEST, HostPortPair("www.google.com", 443))); + NULL, 0, 1, LOWEST, HostPortPair("www.example.org", 443))); scoped_ptr<SpdyFrame> goaway( spdy_util_.ConstructSpdyRstStream(1, RST_STREAM_CANCEL)); MockWrite data_writes[] = { @@ -6580,13 +6652,14 @@ HttpRequestInfo request; request.method = "GET"; - request.url = GURL("https://www.google.com/"); + request.url = GURL("https://www.example.org/"); request.load_flags = 0; MockWrite data_writes[] = { - MockWrite("CONNECT www.google.com:443 HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Proxy-Connection: keep-alive\r\n\r\n"), + MockWrite( + "CONNECT www.example.org:443 HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Proxy-Connection: keep-alive\r\n\r\n"), }; MockRead data_reads[] = { @@ -6626,11 +6699,11 @@ HttpRequestInfo request; request.method = "GET"; - request.url = GURL("https://www.google.com/"); + request.url = GURL("https://www.example.org/"); request.load_flags = 0; scoped_ptr<SpdyFrame> conn(spdy_util_.ConstructSpdyConnect( - NULL, 0, 1, LOWEST, HostPortPair("www.google.com", 443))); + NULL, 0, 1, LOWEST, HostPortPair("www.example.org", 443))); scoped_ptr<SpdyFrame> rst( spdy_util_.ConstructSpdyRstStream(1, RST_STREAM_CANCEL)); MockWrite data_writes[] = { @@ -6684,7 +6757,7 @@ TEST_P(HttpNetworkTransactionTest, BasicAuthSpdyProxy) { HttpRequestInfo request; request.method = "GET"; - request.url = GURL("https://www.google.com/"); + request.url = GURL("https://www.example.org/"); // when the no authentication data flag is set. request.load_flags = net::LOAD_DO_NOT_SEND_AUTH_DATA; @@ -6697,7 +6770,7 @@ // Since we have proxy, should try to establish tunnel. scoped_ptr<SpdyFrame> req(spdy_util_.ConstructSpdyConnect( - NULL, 0, 1, LOWEST, HostPortPair("www.google.com", 443))); + NULL, 0, 1, LOWEST, HostPortPair("www.example.org", 443))); scoped_ptr<SpdyFrame> rst( spdy_util_.ConstructSpdyRstStream(1, RST_STREAM_CANCEL)); @@ -6708,11 +6781,12 @@ }; scoped_ptr<SpdyFrame> connect2(spdy_util_.ConstructSpdyConnect( kAuthCredentials, arraysize(kAuthCredentials) / 2, 3, LOWEST, - HostPortPair("www.google.com", 443))); - // fetch https://www.google.com/ via HTTP - const char get[] = "GET / HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Connection: keep-alive\r\n\r\n"; + HostPortPair("www.example.org", 443))); + // fetch https://www.example.org/ via HTTP + const char get[] = + "GET / HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Connection: keep-alive\r\n\r\n"; scoped_ptr<SpdyFrame> wrapped_get( spdy_util_.ConstructSpdyBodyFrame(3, get, strlen(get), false)); @@ -6825,7 +6899,7 @@ HttpRequestInfo push_request; request.method = "GET"; - request.url = GURL("http://www.google.com/"); + request.url = GURL("http://www.example.org/"); push_request.method = "GET"; push_request.url = GURL("http://www.another-origin.com/foo.dat"); @@ -6942,7 +7016,7 @@ HttpRequestInfo request; request.method = "GET"; - request.url = GURL("http://www.google.com/"); + request.url = GURL("http://www.example.org/"); // Configure against https proxy server "myproxy:70". session_deps_.proxy_service.reset( @@ -7028,14 +7102,15 @@ HttpRequestInfo request; request.method = "GET"; - request.url = GURL("https://www.google.com/"); + request.url = GURL("https://www.example.org/"); request.load_flags = 0; // Attempt to fetch the URL from a server with a bad cert MockWrite bad_cert_writes[] = { - MockWrite("CONNECT www.google.com:443 HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Proxy-Connection: keep-alive\r\n\r\n"), + MockWrite( + "CONNECT www.example.org:443 HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Proxy-Connection: keep-alive\r\n\r\n"), }; MockRead bad_cert_reads[] = { @@ -7045,12 +7120,14 @@ // Attempt to fetch the URL with a good cert MockWrite good_data_writes[] = { - MockWrite("CONNECT www.google.com:443 HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Proxy-Connection: keep-alive\r\n\r\n"), - MockWrite("GET / HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Connection: keep-alive\r\n\r\n"), + MockWrite( + "CONNECT www.example.org:443 HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Proxy-Connection: keep-alive\r\n\r\n"), + MockWrite( + "GET / HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Connection: keep-alive\r\n\r\n"), }; MockRead good_cert_reads[] = { @@ -7106,7 +7183,7 @@ TEST_P(HttpNetworkTransactionTest, BuildRequest_UserAgent) { HttpRequestInfo request; request.method = "GET"; - request.url = GURL("http://www.google.com/"); + request.url = GURL("http://www.example.org/"); request.extra_headers.SetHeader(HttpRequestHeaders::kUserAgent, "Chromium Ultra Awesome X Edition"); @@ -7115,10 +7192,11 @@ new HttpNetworkTransaction(DEFAULT_PRIORITY, session.get())); MockWrite data_writes[] = { - MockWrite("GET / HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Connection: keep-alive\r\n" - "User-Agent: Chromium Ultra Awesome X Edition\r\n\r\n"), + MockWrite( + "GET / HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Connection: keep-alive\r\n" + "User-Agent: Chromium Ultra Awesome X Edition\r\n\r\n"), }; // Lastly, the server responds with the actual content. @@ -7145,7 +7223,7 @@ TEST_P(HttpNetworkTransactionTest, BuildRequest_UserAgentOverTunnel) { HttpRequestInfo request; request.method = "GET"; - request.url = GURL("https://www.google.com/"); + request.url = GURL("https://www.example.org/"); request.extra_headers.SetHeader(HttpRequestHeaders::kUserAgent, "Chromium Ultra Awesome X Edition"); @@ -7155,10 +7233,11 @@ new HttpNetworkTransaction(DEFAULT_PRIORITY, session.get())); MockWrite data_writes[] = { - MockWrite("CONNECT www.google.com:443 HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Proxy-Connection: keep-alive\r\n" - "User-Agent: Chromium Ultra Awesome X Edition\r\n\r\n"), + MockWrite( + "CONNECT www.example.org:443 HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Proxy-Connection: keep-alive\r\n" + "User-Agent: Chromium Ultra Awesome X Edition\r\n\r\n"), }; MockRead data_reads[] = { // Return an error, so the transaction stops here (this test isn't @@ -7184,7 +7263,7 @@ TEST_P(HttpNetworkTransactionTest, BuildRequest_Referer) { HttpRequestInfo request; request.method = "GET"; - request.url = GURL("http://www.google.com/"); + request.url = GURL("http://www.example.org/"); request.load_flags = 0; request.extra_headers.SetHeader(HttpRequestHeaders::kReferer, "http://the.previous.site.com/"); @@ -7194,10 +7273,11 @@ new HttpNetworkTransaction(DEFAULT_PRIORITY, session.get())); MockWrite data_writes[] = { - MockWrite("GET / HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Connection: keep-alive\r\n" - "Referer: http://the.previous.site.com/\r\n\r\n"), + MockWrite( + "GET / HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Connection: keep-alive\r\n" + "Referer: http://the.previous.site.com/\r\n\r\n"), }; // Lastly, the server responds with the actual content. @@ -7224,17 +7304,18 @@ TEST_P(HttpNetworkTransactionTest, BuildRequest_PostContentLengthZero) { HttpRequestInfo request; request.method = "POST"; - request.url = GURL("http://www.google.com/"); + request.url = GURL("http://www.example.org/"); scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps_)); scoped_ptr<HttpTransaction> trans( new HttpNetworkTransaction(DEFAULT_PRIORITY, session.get())); MockWrite data_writes[] = { - MockWrite("POST / HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Connection: keep-alive\r\n" - "Content-Length: 0\r\n\r\n"), + MockWrite( + "POST / HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Connection: keep-alive\r\n" + "Content-Length: 0\r\n\r\n"), }; // Lastly, the server responds with the actual content. @@ -7261,17 +7342,18 @@ TEST_P(HttpNetworkTransactionTest, BuildRequest_PutContentLengthZero) { HttpRequestInfo request; request.method = "PUT"; - request.url = GURL("http://www.google.com/"); + request.url = GURL("http://www.example.org/"); scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps_)); scoped_ptr<HttpTransaction> trans( new HttpNetworkTransaction(DEFAULT_PRIORITY, session.get())); MockWrite data_writes[] = { - MockWrite("PUT / HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Connection: keep-alive\r\n" - "Content-Length: 0\r\n\r\n"), + MockWrite( + "PUT / HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Connection: keep-alive\r\n" + "Content-Length: 0\r\n\r\n"), }; // Lastly, the server responds with the actual content. @@ -7298,17 +7380,18 @@ TEST_P(HttpNetworkTransactionTest, BuildRequest_HeadContentLengthZero) { HttpRequestInfo request; request.method = "HEAD"; - request.url = GURL("http://www.google.com/"); + request.url = GURL("http://www.example.org/"); scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps_)); scoped_ptr<HttpTransaction> trans( new HttpNetworkTransaction(DEFAULT_PRIORITY, session.get())); MockWrite data_writes[] = { - MockWrite("HEAD / HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Connection: keep-alive\r\n" - "Content-Length: 0\r\n\r\n"), + MockWrite( + "HEAD / HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Connection: keep-alive\r\n" + "Content-Length: 0\r\n\r\n"), }; // Lastly, the server responds with the actual content. @@ -7335,7 +7418,7 @@ TEST_P(HttpNetworkTransactionTest, BuildRequest_CacheControlNoCache) { HttpRequestInfo request; request.method = "GET"; - request.url = GURL("http://www.google.com/"); + request.url = GURL("http://www.example.org/"); request.load_flags = LOAD_BYPASS_CACHE; scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps_)); @@ -7343,11 +7426,12 @@ new HttpNetworkTransaction(DEFAULT_PRIORITY, session.get())); MockWrite data_writes[] = { - MockWrite("GET / HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Connection: keep-alive\r\n" - "Pragma: no-cache\r\n" - "Cache-Control: no-cache\r\n\r\n"), + MockWrite( + "GET / HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Connection: keep-alive\r\n" + "Pragma: no-cache\r\n" + "Cache-Control: no-cache\r\n\r\n"), }; // Lastly, the server responds with the actual content. @@ -7375,7 +7459,7 @@ BuildRequest_CacheControlValidateCache) { HttpRequestInfo request; request.method = "GET"; - request.url = GURL("http://www.google.com/"); + request.url = GURL("http://www.example.org/"); request.load_flags = LOAD_VALIDATE_CACHE; scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps_)); @@ -7383,10 +7467,11 @@ new HttpNetworkTransaction(DEFAULT_PRIORITY, session.get())); MockWrite data_writes[] = { - MockWrite("GET / HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Connection: keep-alive\r\n" - "Cache-Control: max-age=0\r\n\r\n"), + MockWrite( + "GET / HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Connection: keep-alive\r\n" + "Cache-Control: max-age=0\r\n\r\n"), }; // Lastly, the server responds with the actual content. @@ -7413,7 +7498,7 @@ TEST_P(HttpNetworkTransactionTest, BuildRequest_ExtraHeaders) { HttpRequestInfo request; request.method = "GET"; - request.url = GURL("http://www.google.com/"); + request.url = GURL("http://www.example.org/"); request.extra_headers.SetHeader("FooHeader", "Bar"); scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps_)); @@ -7421,10 +7506,11 @@ new HttpNetworkTransaction(DEFAULT_PRIORITY, session.get())); MockWrite data_writes[] = { - MockWrite("GET / HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Connection: keep-alive\r\n" - "FooHeader: Bar\r\n\r\n"), + MockWrite( + "GET / HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Connection: keep-alive\r\n" + "FooHeader: Bar\r\n\r\n"), }; // Lastly, the server responds with the actual content. @@ -7451,7 +7537,7 @@ TEST_P(HttpNetworkTransactionTest, BuildRequest_ExtraHeadersStripped) { HttpRequestInfo request; request.method = "GET"; - request.url = GURL("http://www.google.com/"); + request.url = GURL("http://www.example.org/"); request.extra_headers.SetHeader("referer", "www.foo.com"); request.extra_headers.SetHeader("hEllo", "Kitty"); request.extra_headers.SetHeader("FoO", "bar"); @@ -7461,12 +7547,13 @@ new HttpNetworkTransaction(DEFAULT_PRIORITY, session.get())); MockWrite data_writes[] = { - MockWrite("GET / HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Connection: keep-alive\r\n" - "referer: www.foo.com\r\n" - "hEllo: Kitty\r\n" - "FoO: bar\r\n\r\n"), + MockWrite( + "GET / HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Connection: keep-alive\r\n" + "referer: www.foo.com\r\n" + "hEllo: Kitty\r\n" + "FoO: bar\r\n\r\n"), }; // Lastly, the server responds with the actual content. @@ -7493,7 +7580,7 @@ TEST_P(HttpNetworkTransactionTest, SOCKS4_HTTP_GET) { HttpRequestInfo request; request.method = "GET"; - request.url = GURL("http://www.google.com/"); + request.url = GURL("http://www.example.org/"); request.load_flags = 0; session_deps_.proxy_service.reset( @@ -7509,11 +7596,11 @@ char read_buffer[] = { 0x00, 0x5A, 0x00, 0x00, 0, 0, 0, 0 }; MockWrite data_writes[] = { - MockWrite(ASYNC, write_buffer, arraysize(write_buffer)), - MockWrite("GET / HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Connection: keep-alive\r\n\r\n") - }; + MockWrite(ASYNC, write_buffer, arraysize(write_buffer)), + MockWrite( + "GET / HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Connection: keep-alive\r\n\r\n")}; MockRead data_reads[] = { MockRead(ASYNC, read_buffer, arraysize(read_buffer)), @@ -7552,7 +7639,7 @@ TEST_P(HttpNetworkTransactionTest, SOCKS4_SSL_GET) { HttpRequestInfo request; request.method = "GET"; - request.url = GURL("https://www.google.com/"); + request.url = GURL("https://www.example.org/"); request.load_flags = 0; session_deps_.proxy_service.reset( @@ -7568,12 +7655,12 @@ unsigned char read_buffer[] = { 0x00, 0x5A, 0x00, 0x00, 0, 0, 0, 0 }; MockWrite data_writes[] = { - MockWrite(ASYNC, reinterpret_cast<char*>(write_buffer), - arraysize(write_buffer)), - MockWrite("GET / HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Connection: keep-alive\r\n\r\n") - }; + MockWrite(ASYNC, reinterpret_cast<char*>(write_buffer), + arraysize(write_buffer)), + MockWrite( + "GET / HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Connection: keep-alive\r\n\r\n")}; MockRead data_reads[] = { MockRead(ASYNC, reinterpret_cast<char*>(read_buffer), @@ -7616,7 +7703,7 @@ TEST_P(HttpNetworkTransactionTest, SOCKS4_HTTP_GET_no_PAC) { HttpRequestInfo request; request.method = "GET"; - request.url = GURL("http://www.google.com/"); + request.url = GURL("http://www.example.org/"); request.load_flags = 0; session_deps_.proxy_service.reset( @@ -7632,11 +7719,11 @@ char read_buffer[] = { 0x00, 0x5A, 0x00, 0x00, 0, 0, 0, 0 }; MockWrite data_writes[] = { - MockWrite(ASYNC, write_buffer, arraysize(write_buffer)), - MockWrite("GET / HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Connection: keep-alive\r\n\r\n") - }; + MockWrite(ASYNC, write_buffer, arraysize(write_buffer)), + MockWrite( + "GET / HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Connection: keep-alive\r\n\r\n")}; MockRead data_reads[] = { MockRead(ASYNC, read_buffer, arraysize(read_buffer)), @@ -7675,7 +7762,7 @@ TEST_P(HttpNetworkTransactionTest, SOCKS5_HTTP_GET) { HttpRequestInfo request; request.method = "GET"; - request.url = GURL("http://www.google.com/"); + request.url = GURL("http://www.example.org/"); request.load_flags = 0; session_deps_.proxy_service.reset( @@ -7690,25 +7777,24 @@ const char kSOCKS5GreetRequest[] = { 0x05, 0x01, 0x00 }; const char kSOCKS5GreetResponse[] = { 0x05, 0x00 }; const char kSOCKS5OkRequest[] = { - 0x05, // Version - 0x01, // Command (CONNECT) - 0x00, // Reserved. - 0x03, // Address type (DOMAINNAME). - 0x0E, // Length of domain (14) - // Domain string: - 'w', 'w', 'w', '.', 'g', 'o', 'o', 'g', 'l', 'e', '.', 'c', 'o', 'm', - 0x00, 0x50, // 16-bit port (80) + 0x05, // Version + 0x01, // Command (CONNECT) + 0x00, // Reserved. + 0x03, // Address type (DOMAINNAME). + 0x0F, // Length of domain (15) + 'w', 'w', 'w', '.', 'e', 'x', 'a', 'm', 'p', 'l', 'e', // Domain string + '.', 'o', 'r', 'g', 0x00, 0x50, // 16-bit port (80) }; const char kSOCKS5OkResponse[] = { 0x05, 0x00, 0x00, 0x01, 127, 0, 0, 1, 0x00, 0x50 }; MockWrite data_writes[] = { - MockWrite(ASYNC, kSOCKS5GreetRequest, arraysize(kSOCKS5GreetRequest)), - MockWrite(ASYNC, kSOCKS5OkRequest, arraysize(kSOCKS5OkRequest)), - MockWrite("GET / HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Connection: keep-alive\r\n\r\n") - }; + MockWrite(ASYNC, kSOCKS5GreetRequest, arraysize(kSOCKS5GreetRequest)), + MockWrite(ASYNC, kSOCKS5OkRequest, arraysize(kSOCKS5OkRequest)), + MockWrite( + "GET / HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Connection: keep-alive\r\n\r\n")}; MockRead data_reads[] = { MockRead(ASYNC, kSOCKS5GreetResponse, arraysize(kSOCKS5GreetResponse)), @@ -7748,7 +7834,7 @@ TEST_P(HttpNetworkTransactionTest, SOCKS5_SSL_GET) { HttpRequestInfo request; request.method = "GET"; - request.url = GURL("https://www.google.com/"); + request.url = GURL("https://www.example.org/"); request.load_flags = 0; session_deps_.proxy_service.reset( @@ -7763,27 +7849,26 @@ const char kSOCKS5GreetRequest[] = { 0x05, 0x01, 0x00 }; const char kSOCKS5GreetResponse[] = { 0x05, 0x00 }; const unsigned char kSOCKS5OkRequest[] = { - 0x05, // Version - 0x01, // Command (CONNECT) - 0x00, // Reserved. - 0x03, // Address type (DOMAINNAME). - 0x0E, // Length of domain (14) - // Domain string: - 'w', 'w', 'w', '.', 'g', 'o', 'o', 'g', 'l', 'e', '.', 'c', 'o', 'm', - 0x01, 0xBB, // 16-bit port (443) + 0x05, // Version + 0x01, // Command (CONNECT) + 0x00, // Reserved. + 0x03, // Address type (DOMAINNAME). + 0x0F, // Length of domain (15) + 'w', 'w', 'w', '.', 'e', 'x', 'a', 'm', 'p', 'l', 'e', // Domain string + '.', 'o', 'r', 'g', 0x01, 0xBB, // 16-bit port (443) }; const char kSOCKS5OkResponse[] = { 0x05, 0x00, 0x00, 0x01, 0, 0, 0, 0, 0x00, 0x00 }; MockWrite data_writes[] = { - MockWrite(ASYNC, kSOCKS5GreetRequest, arraysize(kSOCKS5GreetRequest)), - MockWrite(ASYNC, reinterpret_cast<const char*>(kSOCKS5OkRequest), - arraysize(kSOCKS5OkRequest)), - MockWrite("GET / HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Connection: keep-alive\r\n\r\n") - }; + MockWrite(ASYNC, kSOCKS5GreetRequest, arraysize(kSOCKS5GreetRequest)), + MockWrite(ASYNC, reinterpret_cast<const char*>(kSOCKS5OkRequest), + arraysize(kSOCKS5OkRequest)), + MockWrite( + "GET / HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Connection: keep-alive\r\n\r\n")}; MockRead data_reads[] = { MockRead(ASYNC, kSOCKS5GreetResponse, arraysize(kSOCKS5GreetResponse)), @@ -7870,38 +7955,38 @@ TEST_P(HttpNetworkTransactionTest, GroupNameForDirectConnections) { const GroupNameTest tests[] = { - { - "", // unused - "http://www.google.com/direct", - "www.google.com:80", - false, - }, - { - "", // unused - "http://[2001:1418:13:1::25]/direct", - "[2001:1418:13:1::25]:80", - false, - }, + { + "", // unused + "http://www.example.org/direct", + "www.example.org:80", + false, + }, + { + "", // unused + "http://[2001:1418:13:1::25]/direct", + "[2001:1418:13:1::25]:80", + false, + }, - // SSL Tests - { - "", // unused - "https://www.google.com/direct_ssl", - "ssl/www.google.com:443", - true, - }, - { - "", // unused - "https://[2001:1418:13:1::25]/direct", - "ssl/[2001:1418:13:1::25]:443", - true, - }, - { - "", // unused - "http://host.with.alternate/direct", - "ssl/host.with.alternate:443", - true, - }, + // SSL Tests + { + "", // unused + "https://www.example.org/direct_ssl", + "ssl/www.example.org:443", + true, + }, + { + "", // unused + "https://[2001:1418:13:1::25]/direct", + "ssl/[2001:1418:13:1::25]:443", + true, + }, + { + "", // unused + "http://host.with.alternate/direct", + "ssl/host.with.alternate:443", + true, + }, }; session_deps_.use_alternate_protocols = true; @@ -7937,34 +8022,34 @@ TEST_P(HttpNetworkTransactionTest, GroupNameForHTTPProxyConnections) { const GroupNameTest tests[] = { - { - "http_proxy", - "http://www.google.com/http_proxy_normal", - "www.google.com:80", - false, - }, + { + "http_proxy", + "http://www.example.org/http_proxy_normal", + "www.example.org:80", + false, + }, - // SSL Tests - { - "http_proxy", - "https://www.google.com/http_connect_ssl", - "ssl/www.google.com:443", - true, - }, + // SSL Tests + { + "http_proxy", + "https://www.example.org/http_connect_ssl", + "ssl/www.example.org:443", + true, + }, - { - "http_proxy", - "http://host.with.alternate/direct", - "ssl/host.with.alternate:443", - true, - }, + { + "http_proxy", + "http://host.with.alternate/direct", + "ssl/host.with.alternate:443", + true, + }, - { - "http_proxy", - "ftp://ftp.google.com/http_proxy_normal", - "ftp/ftp.google.com:21", - false, - }, + { + "http_proxy", + "ftp://ftp.google.com/http_proxy_normal", + "ftp/ftp.google.com:21", + false, + }, }; session_deps_.use_alternate_protocols = true; @@ -8002,39 +8087,39 @@ TEST_P(HttpNetworkTransactionTest, GroupNameForSOCKSConnections) { const GroupNameTest tests[] = { - { - "socks4://socks_proxy:1080", - "http://www.google.com/socks4_direct", - "socks4/www.google.com:80", - false, - }, - { - "socks5://socks_proxy:1080", - "http://www.google.com/socks5_direct", - "socks5/www.google.com:80", - false, - }, + { + "socks4://socks_proxy:1080", + "http://www.example.org/socks4_direct", + "socks4/www.example.org:80", + false, + }, + { + "socks5://socks_proxy:1080", + "http://www.example.org/socks5_direct", + "socks5/www.example.org:80", + false, + }, - // SSL Tests - { - "socks4://socks_proxy:1080", - "https://www.google.com/socks4_ssl", - "socks4/ssl/www.google.com:443", - true, - }, - { - "socks5://socks_proxy:1080", - "https://www.google.com/socks5_ssl", - "socks5/ssl/www.google.com:443", - true, - }, + // SSL Tests + { + "socks4://socks_proxy:1080", + "https://www.example.org/socks4_ssl", + "socks4/ssl/www.example.org:443", + true, + }, + { + "socks5://socks_proxy:1080", + "https://www.example.org/socks5_ssl", + "socks5/ssl/www.example.org:443", + true, + }, - { - "socks4://socks_proxy:1080", - "http://host.with.alternate/direct", - "socks4/ssl/host.with.alternate:443", - true, - }, + { + "socks4://socks_proxy:1080", + "http://host.with.alternate/direct", + "socks4/ssl/host.with.alternate:443", + true, + }, }; session_deps_.use_alternate_protocols = true; @@ -8076,7 +8161,7 @@ TEST_P(HttpNetworkTransactionTest, ReconsiderProxyAfterFailedConnection) { HttpRequestInfo request; request.method = "GET"; - request.url = GURL("http://www.google.com/"); + request.url = GURL("http://www.example.org/"); session_deps_.proxy_service.reset( ProxyService::CreateFixed("myproxy:70;foobar:80")); @@ -8106,7 +8191,7 @@ HttpRequestInfo request; request.method = "GET"; request.load_flags = load_flags; - request.url = GURL("http://www.google.com/"); + request.url = GURL("http://www.example.org/"); // Select a host resolver that does caching. session_deps_.host_resolver.reset(new MockCachingHostResolver); @@ -8115,16 +8200,12 @@ scoped_ptr<HttpTransaction> trans( new HttpNetworkTransaction(DEFAULT_PRIORITY, session.get())); - // Warm up the host cache so it has an entry for "www.google.com". + // Warm up the host cache so it has an entry for "www.example.org". AddressList addrlist; TestCompletionCallback callback; int rv = session_deps_.host_resolver->Resolve( - HostResolver::RequestInfo(HostPortPair("www.google.com", 80)), - DEFAULT_PRIORITY, - &addrlist, - callback.callback(), - NULL, - BoundNetLog()); + HostResolver::RequestInfo(HostPortPair("www.example.org", 80)), + DEFAULT_PRIORITY, &addrlist, callback.callback(), NULL, BoundNetLog()); EXPECT_EQ(ERR_IO_PENDING, rv); rv = callback.WaitForResult(); EXPECT_EQ(OK, rv); @@ -8132,18 +8213,14 @@ // Verify that it was added to host cache, by doing a subsequent async lookup // and confirming it completes synchronously. rv = session_deps_.host_resolver->Resolve( - HostResolver::RequestInfo(HostPortPair("www.google.com", 80)), - DEFAULT_PRIORITY, - &addrlist, - callback.callback(), - NULL, - BoundNetLog()); + HostResolver::RequestInfo(HostPortPair("www.example.org", 80)), + DEFAULT_PRIORITY, &addrlist, callback.callback(), NULL, BoundNetLog()); ASSERT_EQ(OK, rv); - // Inject a failure the next time that "www.google.com" is resolved. This way + // Inject a failure the next time that "www.example.org" is resolved. This way // we can tell if the next lookup hit the cache, or the "network". // (cache --> success, "network" --> failure). - session_deps_.host_resolver->rules()->AddSimulatedFailure("www.google.com"); + session_deps_.host_resolver->rules()->AddSimulatedFailure("www.example.org"); // Connect up a mock socket which will fail with ERR_UNEXPECTED during the // first read -- this won't be reached as the host resolution will fail first. @@ -8157,7 +8234,7 @@ rv = callback.WaitForResult(); // If we bypassed the cache, we would have gotten a failure while resolving - // "www.google.com". + // "www.example.org". EXPECT_EQ(ERR_NAME_NOT_RESOLVED, rv); } @@ -8246,13 +8323,14 @@ TEST_P(HttpNetworkTransactionTest, DrainResetOK) { HttpRequestInfo request; request.method = "GET"; - request.url = GURL("http://www.google.com/"); + request.url = GURL("http://www.example.org/"); request.load_flags = 0; MockWrite data_writes1[] = { - MockWrite("GET / HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Connection: keep-alive\r\n\r\n"), + MockWrite( + "GET / HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Connection: keep-alive\r\n\r\n"), }; MockRead data_reads1[] = { @@ -8271,10 +8349,11 @@ // After calling trans->RestartWithAuth(), this is the request we should // be issuing -- the final header line contains the credentials. MockWrite data_writes2[] = { - MockWrite("GET / HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Connection: keep-alive\r\n" - "Authorization: Basic Zm9vOmJhcg==\r\n\r\n"), + MockWrite( + "GET / HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Connection: keep-alive\r\n" + "Authorization: Basic Zm9vOmJhcg==\r\n\r\n"), }; // Lastly, the server responds with the actual content. @@ -8326,7 +8405,7 @@ HttpRequestInfo request; request.method = "GET"; - request.url = GURL("https://www.google.com/"); + request.url = GURL("https://www.example.org/"); request.load_flags = 0; MockRead proxy_reads[] = { @@ -8358,7 +8437,7 @@ TEST_P(HttpNetworkTransactionTest, LargeContentLengthThenClose) { HttpRequestInfo request; request.method = "GET"; - request.url = GURL("http://www.google.com/"); + request.url = GURL("http://www.example.org/"); request.load_flags = 0; scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps_)); @@ -8409,7 +8488,7 @@ HttpRequestInfo request; request.method = "POST"; - request.url = GURL("http://www.google.com/upload"); + request.url = GURL("http://www.example.org/upload"); request.upload_data_stream = &upload_data_stream; request.load_flags = 0; @@ -8466,7 +8545,7 @@ HttpRequestInfo request; request.method = "POST"; - request.url = GURL("http://www.google.com/upload"); + request.url = GURL("http://www.example.org/upload"); request.upload_data_stream = &upload_data_stream; request.load_flags = 0; @@ -8524,7 +8603,7 @@ HttpRequestInfo request; request.method = "POST"; - request.url = GURL("http://www.google.com/upload"); + request.url = GURL("http://www.example.org/upload"); request.upload_data_stream = &upload_data_stream; request.load_flags = 0; @@ -8553,16 +8632,17 @@ HttpRequestInfo request; request.method = "GET"; - request.url = GURL("http://www.google.com/"); + request.url = GURL("http://www.example.org/"); request.load_flags = 0; // First transaction will request a resource and receive a Basic challenge // with realm="first_realm". MockWrite data_writes1[] = { - MockWrite("GET / HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Connection: keep-alive\r\n" - "\r\n"), + MockWrite( + "GET / HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Connection: keep-alive\r\n" + "\r\n"), }; MockRead data_reads1[] = { MockRead("HTTP/1.1 401 Unauthorized\r\n" @@ -8574,11 +8654,12 @@ // for first_realm. The server will reject and provide a challenge with // second_realm. MockWrite data_writes2[] = { - MockWrite("GET / HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Connection: keep-alive\r\n" - "Authorization: Basic Zmlyc3Q6YmF6\r\n" - "\r\n"), + MockWrite( + "GET / HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Connection: keep-alive\r\n" + "Authorization: Basic Zmlyc3Q6YmF6\r\n" + "\r\n"), }; MockRead data_reads2[] = { MockRead("HTTP/1.1 401 Unauthorized\r\n" @@ -8589,11 +8670,12 @@ // This again fails, and goes back to first_realm. Make sure that the // entry is removed from cache. MockWrite data_writes3[] = { - MockWrite("GET / HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Connection: keep-alive\r\n" - "Authorization: Basic c2Vjb25kOmZvdQ==\r\n" - "\r\n"), + MockWrite( + "GET / HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Connection: keep-alive\r\n" + "Authorization: Basic c2Vjb25kOmZvdQ==\r\n" + "\r\n"), }; MockRead data_reads3[] = { MockRead("HTTP/1.1 401 Unauthorized\r\n" @@ -8603,11 +8685,12 @@ // Try one last time (with the correct password) and get the resource. MockWrite data_writes4[] = { - MockWrite("GET / HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Connection: keep-alive\r\n" - "Authorization: Basic Zmlyc3Q6YmFy\r\n" - "\r\n"), + MockWrite( + "GET / HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Connection: keep-alive\r\n" + "Authorization: Basic Zmlyc3Q6YmFy\r\n" + "\r\n"), }; MockRead data_reads4[] = { MockRead("HTTP/1.1 200 OK\r\n" @@ -8648,7 +8731,7 @@ const AuthChallengeInfo* challenge = response->auth_challenge.get(); ASSERT_FALSE(challenge == NULL); EXPECT_FALSE(challenge->is_proxy); - EXPECT_EQ("www.google.com:80", challenge->challenger.ToString()); + EXPECT_EQ("www.example.org:80", challenge->challenger.ToString()); EXPECT_EQ("first_realm", challenge->realm); EXPECT_EQ("basic", challenge->scheme); @@ -8666,7 +8749,7 @@ challenge = response->auth_challenge.get(); ASSERT_FALSE(challenge == NULL); EXPECT_FALSE(challenge->is_proxy); - EXPECT_EQ("www.google.com:80", challenge->challenger.ToString()); + EXPECT_EQ("www.example.org:80", challenge->challenger.ToString()); EXPECT_EQ("second_realm", challenge->realm); EXPECT_EQ("basic", challenge->scheme); @@ -8685,7 +8768,7 @@ challenge = response->auth_challenge.get(); ASSERT_FALSE(challenge == NULL); EXPECT_FALSE(challenge->is_proxy); - EXPECT_EQ("www.google.com:80", challenge->challenger.ToString()); + EXPECT_EQ("www.example.org:80", challenge->challenger.ToString()); EXPECT_EQ("first_realm", challenge->realm); EXPECT_EQ("basic", challenge->scheme); @@ -8717,7 +8800,7 @@ HttpRequestInfo request; request.method = "GET"; - request.url = GURL("http://www.google.com/"); + request.url = GURL("http://www.example.org/"); request.load_flags = 0; StaticSocketDataProvider data(data_reads, arraysize(data_reads), NULL, 0); @@ -8733,7 +8816,7 @@ int rv = trans->Start(&request, callback.callback(), BoundNetLog()); EXPECT_EQ(ERR_IO_PENDING, rv); - HostPortPair http_host_port_pair("www.google.com", 80); + HostPortPair http_host_port_pair("www.example.org", 80); HttpServerProperties& http_server_properties = *session->http_server_properties(); AlternativeService alternative_service = @@ -8773,7 +8856,7 @@ HttpRequestInfo request; request.method = "GET"; - request.url = GURL("http://www.google.com/"); + request.url = GURL("http://www.example.org/"); request.load_flags = 0; StaticSocketDataProvider data(data_reads, arraysize(data_reads), NULL, 0); @@ -8784,7 +8867,7 @@ scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps_)); - HostPortPair http_host_port_pair("www.google.com", 80); + HostPortPair http_host_port_pair("www.example.org", 80); HttpServerProperties& http_server_properties = *session->http_server_properties(); AlternativeService alternative_service(QUIC, "", 80); @@ -8825,7 +8908,7 @@ HttpRequestInfo request; request.method = "GET"; - request.url = GURL("http://www.google.com/"); + request.url = GURL("http://www.example.org/"); request.load_flags = 0; MockConnect mock_connect(ASYNC, ERR_CONNECTION_REFUSED); @@ -8850,7 +8933,7 @@ // Port must be < 1024, or the header will be ignored (since initial port was // port 80 (another restricted port). AlternativeService alternative_service( - AlternateProtocolFromNextProto(GetParam()), "www.google.com", + AlternateProtocolFromNextProto(GetParam()), "www.example.org", 666); /* port is ignored by MockConnect anyway */ http_server_properties->SetAlternativeService(host_port_pair, alternative_service, 1.0); @@ -8889,7 +8972,7 @@ HttpRequestInfo restricted_port_request; restricted_port_request.method = "GET"; - restricted_port_request.url = GURL("http://www.google.com:1023/"); + restricted_port_request.url = GURL("http://www.example.org:1023/"); restricted_port_request.load_flags = 0; MockConnect mock_connect(ASYNC, ERR_CONNECTION_REFUSED); @@ -8912,7 +8995,7 @@ session->http_server_properties(); const int kUnrestrictedAlternatePort = 1024; AlternativeService alternative_service( - AlternateProtocolFromNextProto(GetParam()), "www.google.com", + AlternateProtocolFromNextProto(GetParam()), "www.example.org", kUnrestrictedAlternatePort); http_server_properties->SetAlternativeService( HostPortPair::FromURL(restricted_port_request.url), alternative_service, @@ -8942,7 +9025,7 @@ HttpRequestInfo restricted_port_request; restricted_port_request.method = "GET"; - restricted_port_request.url = GURL("http://www.google.com:1023/"); + restricted_port_request.url = GURL("http://www.example.org:1023/"); restricted_port_request.load_flags = 0; MockConnect mock_connect(ASYNC, ERR_CONNECTION_REFUSED); @@ -8965,7 +9048,7 @@ session->http_server_properties(); const int kUnrestrictedAlternatePort = 1024; AlternativeService alternative_service( - AlternateProtocolFromNextProto(GetParam()), "www.google.com", + AlternateProtocolFromNextProto(GetParam()), "www.example.org", kUnrestrictedAlternatePort); http_server_properties->SetAlternativeService( HostPortPair::FromURL(restricted_port_request.url), alternative_service, @@ -8992,7 +9075,7 @@ HttpRequestInfo restricted_port_request; restricted_port_request.method = "GET"; - restricted_port_request.url = GURL("http://www.google.com:1023/"); + restricted_port_request.url = GURL("http://www.example.org:1023/"); restricted_port_request.load_flags = 0; MockConnect mock_connect(ASYNC, ERR_CONNECTION_REFUSED); @@ -9015,7 +9098,7 @@ session->http_server_properties(); const int kRestrictedAlternatePort = 80; AlternativeService alternative_service( - AlternateProtocolFromNextProto(GetParam()), "www.google.com", + AlternateProtocolFromNextProto(GetParam()), "www.example.org", kRestrictedAlternatePort); http_server_properties->SetAlternativeService( HostPortPair::FromURL(restricted_port_request.url), alternative_service, @@ -9043,7 +9126,7 @@ HttpRequestInfo unrestricted_port_request; unrestricted_port_request.method = "GET"; - unrestricted_port_request.url = GURL("http://www.google.com:1024/"); + unrestricted_port_request.url = GURL("http://www.example.org:1024/"); unrestricted_port_request.load_flags = 0; MockConnect mock_connect(ASYNC, ERR_CONNECTION_REFUSED); @@ -9066,7 +9149,7 @@ session->http_server_properties(); const int kRestrictedAlternatePort = 80; AlternativeService alternative_service( - AlternateProtocolFromNextProto(GetParam()), "www.google.com", + AlternateProtocolFromNextProto(GetParam()), "www.example.org", kRestrictedAlternatePort); http_server_properties->SetAlternativeService( HostPortPair::FromURL(unrestricted_port_request.url), alternative_service, @@ -9093,7 +9176,7 @@ HttpRequestInfo unrestricted_port_request; unrestricted_port_request.method = "GET"; - unrestricted_port_request.url = GURL("http://www.google.com:1024/"); + unrestricted_port_request.url = GURL("http://www.example.org:1024/"); unrestricted_port_request.load_flags = 0; MockConnect mock_connect(ASYNC, ERR_CONNECTION_REFUSED); @@ -9116,7 +9199,7 @@ session->http_server_properties(); const int kUnrestrictedAlternatePort = 1024; AlternativeService alternative_service( - AlternateProtocolFromNextProto(GetParam()), "www.google.com", + AlternateProtocolFromNextProto(GetParam()), "www.example.org", kUnrestrictedAlternatePort); http_server_properties->SetAlternativeService( HostPortPair::FromURL(unrestricted_port_request.url), alternative_service, @@ -9141,7 +9224,7 @@ HttpRequestInfo request; request.method = "GET"; - request.url = GURL("http://www.google.com/"); + request.url = GURL("http://www.example.org/"); request.load_flags = 0; // The alternate protocol request will error out before we attempt to connect, @@ -9161,7 +9244,7 @@ session->http_server_properties(); const int kUnsafePort = 7; AlternativeService alternative_service( - AlternateProtocolFromNextProto(GetParam()), "www.google.com", + AlternateProtocolFromNextProto(GetParam()), "www.example.org", kUnsafePort); http_server_properties->SetAlternativeService( HostPortPair::FromURL(request.url), alternative_service, 1.0); @@ -9194,7 +9277,7 @@ HttpRequestInfo request; request.method = "GET"; - request.url = GURL("http://www.google.com/"); + request.url = GURL("http://www.example.org/"); request.load_flags = 0; std::string alternate_protocol_http_header = @@ -9214,6 +9297,8 @@ SSLSocketDataProvider ssl(ASYNC, OK); ssl.SetNextProto(GetParam()); + ssl.cert = ImportCertFromFile(GetTestCertsDirectory(), "spdy_pooling.pem"); + ASSERT_TRUE(ssl.cert.get()); session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl); scoped_ptr<SpdyFrame> req( @@ -9284,7 +9369,7 @@ HttpRequestInfo request; request.method = "GET"; - request.url = GURL("http://www.google.com/"); + request.url = GURL("http://www.example.org/"); request.load_flags = 0; std::string alternate_protocol_http_header = @@ -9314,6 +9399,8 @@ SSLSocketDataProvider ssl(ASYNC, OK); ssl.SetNextProto(GetParam()); + ssl.cert = ImportCertFromFile(GetTestCertsDirectory(), "spdy_pooling.pem"); + ASSERT_TRUE(ssl.cert.get()); session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl); scoped_ptr<SpdyFrame> req1( @@ -9401,7 +9488,7 @@ HttpRequestInfo request; request.method = "GET"; - request.url = GURL("http://www.google.com/"); + request.url = GURL("http://www.example.org/"); request.load_flags = 0; std::string alternate_protocol_http_header = @@ -9529,7 +9616,7 @@ HttpRequestInfo request; request.method = "GET"; - request.url = GURL("http://www.google.com/"); + request.url = GURL("http://www.example.org/"); request.load_flags = 0; std::string alternate_protocol_http_header = @@ -9549,15 +9636,18 @@ SSLSocketDataProvider ssl(ASYNC, OK); ssl.SetNextProto(GetParam()); + ssl.cert = ImportCertFromFile(GetTestCertsDirectory(), "spdy_pooling.pem"); + ASSERT_TRUE(ssl.cert.get()); session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl); scoped_ptr<SpdyFrame> req( spdy_util_.ConstructSpdyGet(NULL, 0, false, 1, LOWEST, true)); MockWrite spdy_writes[] = { - MockWrite("CONNECT www.google.com:443 HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Proxy-Connection: keep-alive\r\n\r\n"), // 0 - CreateMockWrite(*req), // 3 + MockWrite( + "CONNECT www.example.org:443 HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Proxy-Connection: keep-alive\r\n\r\n"), // 0 + CreateMockWrite(*req), // 3 }; const char kCONNECTResponse[] = "HTTP/1.1 200 Connected\r\n\r\n"; @@ -9621,9 +9711,9 @@ ASSERT_EQ(OK, ReadTransaction(trans.get(), &response_data)); EXPECT_EQ("hello!", response_data); ASSERT_EQ(3u, capturing_proxy_resolver.resolved().size()); - EXPECT_EQ("http://www.google.com/", + EXPECT_EQ("http://www.example.org/", capturing_proxy_resolver.resolved()[0].spec()); - EXPECT_EQ("https://www.google.com/", + EXPECT_EQ("https://www.example.org/", capturing_proxy_resolver.resolved()[1].spec()); LoadTimingInfo load_timing_info; @@ -9639,7 +9729,7 @@ HttpRequestInfo request; request.method = "GET"; - request.url = GURL("http://www.google.com/"); + request.url = GURL("http://www.example.org/"); request.load_flags = 0; std::string alternate_protocol_http_header = @@ -9658,6 +9748,8 @@ SSLSocketDataProvider ssl(ASYNC, OK); ssl.SetNextProto(GetParam()); + ssl.cert = ImportCertFromFile(GetTestCertsDirectory(), "spdy_pooling.pem"); + ASSERT_TRUE(ssl.cert.get()); session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl); scoped_ptr<SpdyFrame> req( @@ -9699,7 +9791,7 @@ EXPECT_EQ("hello world", response_data); // Set up an initial SpdySession in the pool to reuse. - HostPortPair host_port_pair("www.google.com", 443); + HostPortPair host_port_pair("www.example.org", 443); SpdySessionKey key(host_port_pair, ProxyServer::Direct(), PRIVACY_MODE_DISABLED); base::WeakPtr<SpdySession> spdy_session = @@ -10329,13 +10421,14 @@ HttpRequestInfo request; request.method = "GET"; - request.url = GURL("https://www.google.com/"); + request.url = GURL("https://www.example.org/"); request.load_flags = 0; MockWrite data_writes[] = { - MockWrite("GET / HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Connection: keep-alive\r\n\r\n"), + MockWrite( + "GET / HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Connection: keep-alive\r\n\r\n"), }; std::string alternate_protocol_http_header = @@ -10392,7 +10485,7 @@ HttpRequestInfo request; request.method = "GET"; - request.url = GURL("https://www.google.com/"); + request.url = GURL("https://www.example.org/"); request.load_flags = 0; SSLSocketDataProvider ssl(ASYNC, OK); @@ -10470,15 +10563,16 @@ HttpRequestInfo request; request.method = "GET"; - request.url = GURL("http://www.google.com"); + request.url = GURL("http://www.example.org"); request.load_flags = 0; // First round goes unauthenticated through the proxy. MockWrite data_writes_1[] = { - MockWrite("GET http://www.google.com/ HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Proxy-Connection: keep-alive\r\n" - "\r\n"), + MockWrite( + "GET http://www.example.org/ HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Proxy-Connection: keep-alive\r\n" + "\r\n"), }; MockRead data_reads_1[] = { MockRead(SYNCHRONOUS, ERR_TEST_PEER_CLOSE_AFTER_NEXT_MOCK_READ), @@ -10492,10 +10586,10 @@ StaticSocketDataProvider data_1(data_reads_1, arraysize(data_reads_1), data_writes_1, arraysize(data_writes_1)); - // Second round tries to tunnel to www.google.com due to the + // Second round tries to tunnel to www.example.org due to the // Alternate-Protocol announcement in the first round. It fails due // to a proxy authentication challenge. - // After the failure, a tunnel is established to www.google.com using + // After the failure, a tunnel is established to www.example.org using // Proxy-Authorization headers. There is then a SPDY request round. // // NOTE: Despite the "Proxy-Connection: Close", these are done on the @@ -10515,21 +10609,23 @@ scoped_ptr<SpdyFrame> data(spdy_util_.ConstructSpdyBodyFrame(1, true)); MockWrite data_writes_2[] = { - // First connection attempt without Proxy-Authorization. - MockWrite("CONNECT www.google.com:443 HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Proxy-Connection: keep-alive\r\n" - "\r\n"), + // First connection attempt without Proxy-Authorization. + MockWrite( + "CONNECT www.example.org:443 HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Proxy-Connection: keep-alive\r\n" + "\r\n"), - // Second connection attempt with Proxy-Authorization. - MockWrite("CONNECT www.google.com:443 HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Proxy-Connection: keep-alive\r\n" - "Proxy-Authorization: auth_token\r\n" - "\r\n"), + // Second connection attempt with Proxy-Authorization. + MockWrite( + "CONNECT www.example.org:443 HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Proxy-Connection: keep-alive\r\n" + "Proxy-Authorization: auth_token\r\n" + "\r\n"), - // SPDY request - CreateMockWrite(*req), + // SPDY request + CreateMockWrite(*req), }; const char kRejectConnectResponse[] = ("HTTP/1.1 407 Unauthorized\r\n" "Proxy-Authenticate: Mock\r\n" @@ -10557,6 +10653,8 @@ SSLSocketDataProvider ssl(ASYNC, OK); ssl.SetNextProto(GetParam()); + ssl.cert = ImportCertFromFile(GetTestCertsDirectory(), "spdy_pooling.pem"); + ASSERT_TRUE(ssl.cert.get()); MockConnect never_finishing_connect(SYNCHRONOUS, ERR_IO_PENDING); StaticSocketDataProvider hanging_non_alternate_protocol_socket( @@ -10600,7 +10698,7 @@ // After all that work, these two lines (or actually, just the scheme) are // what this test is all about. Make sure it happens correctly. EXPECT_EQ("https", request_url.scheme()); - EXPECT_EQ("www.google.com", request_url.host()); + EXPECT_EQ("www.example.org", request_url.host()); LoadTimingInfo load_timing_info; EXPECT_TRUE(trans_2->GetLoadTimingInfo(&load_timing_info)); @@ -10625,7 +10723,7 @@ HttpRequestInfo request; request.method = "GET"; - request.url = GURL("http://www.google.com/"); + request.url = GURL("http://www.example.org/"); request.load_flags = 0; session_deps_.host_resolver->set_synchronous_mode(true); @@ -10672,7 +10770,7 @@ { HttpRequestInfo request; request.method = "GET"; - request.url = GURL("http://www.google.com/"); + request.url = GURL("http://www.example.org/"); request.load_flags = 0; HttpNetworkTransaction trans(DEFAULT_PRIORITY, session.get()); @@ -10707,12 +10805,13 @@ HttpRequestInfo request; request.method = "GET"; - request.url = GURL("http://www.google.com/"); + request.url = GURL("http://www.example.org/"); MockWrite data_writes1[] = { - MockWrite("GET http://www.google.com/ HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Proxy-Connection: keep-alive\r\n\r\n"), + MockWrite( + "GET http://www.example.org/ HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Proxy-Connection: keep-alive\r\n\r\n"), }; MockRead data_reads1[] = { @@ -10770,17 +10869,19 @@ HttpRequestInfo request; request.method = "GET"; - request.url = GURL("https://www.google.com/"); + request.url = GURL("https://www.example.org/"); // Since we have proxy, should try to establish tunnel. MockWrite data_writes1[] = { - MockWrite("CONNECT www.google.com:443 HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Proxy-Connection: keep-alive\r\n\r\n"), + MockWrite( + "CONNECT www.example.org:443 HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Proxy-Connection: keep-alive\r\n\r\n"), - MockWrite("GET / HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Connection: keep-alive\r\n\r\n"), + MockWrite( + "GET / HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Connection: keep-alive\r\n\r\n"), }; MockRead data_reads1[] = { @@ -10845,17 +10946,19 @@ HttpRequestInfo request; request.method = "GET"; - request.url = GURL("https://www.google.com/"); + request.url = GURL("https://www.example.org/"); // Since we have proxy, should try to establish tunnel. MockWrite data_writes1[] = { - MockWrite("CONNECT www.google.com:443 HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Proxy-Connection: keep-alive\r\n\r\n"), + MockWrite( + "CONNECT www.example.org:443 HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Proxy-Connection: keep-alive\r\n\r\n"), - MockWrite("GET / HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Connection: keep-alive\r\n\r\n"), + MockWrite( + "GET / HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Connection: keep-alive\r\n\r\n"), }; MockRead data_reads1[] = { @@ -10894,7 +10997,7 @@ // Test for crbug.com/55424. TEST_P(HttpNetworkTransactionTest, PreconnectWithExistingSpdySession) { scoped_ptr<SpdyFrame> req( - spdy_util_.ConstructSpdyGet("https://www.google.com", false, 1, LOWEST)); + spdy_util_.ConstructSpdyGet("https://www.example.org", false, 1, LOWEST)); MockWrite spdy_writes[] = { CreateMockWrite(*req) }; scoped_ptr<SpdyFrame> resp(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1)); @@ -10918,7 +11021,7 @@ scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps_)); // Set up an initial SpdySession in the pool to reuse. - HostPortPair host_port_pair("www.google.com", 443); + HostPortPair host_port_pair("www.example.org", 443); SpdySessionKey key(host_port_pair, ProxyServer::Direct(), PRIVACY_MODE_DISABLED); base::WeakPtr<SpdySession> spdy_session = @@ -10926,7 +11029,7 @@ HttpRequestInfo request; request.method = "GET"; - request.url = GURL("https://www.google.com/"); + request.url = GURL("https://www.example.org/"); request.load_flags = 0; // This is the important line that marks this as a preconnect. @@ -11346,7 +11449,7 @@ session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl); scoped_ptr<SpdyFrame> host1_req( - spdy_util_.ConstructSpdyGet("https://www.google.com", false, 1, LOWEST)); + spdy_util_.ConstructSpdyGet("https://www.example.org", false, 1, LOWEST)); scoped_ptr<SpdyFrame> host2_req( spdy_util_.ConstructSpdyGet("https://www.gmail.com", false, 3, LOWEST)); MockWrite spdy_writes[] = { @@ -11382,7 +11485,7 @@ TestCompletionCallback callback; HttpRequestInfo request1; request1.method = "GET"; - request1.url = GURL("https://www.google.com/"); + request1.url = GURL("https://www.example.org/"); request1.load_flags = 0; HttpNetworkTransaction trans1(DEFAULT_PRIORITY, session.get()); @@ -11449,7 +11552,7 @@ session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl); scoped_ptr<SpdyFrame> host1_req( - spdy_util_.ConstructSpdyGet("https://www.google.com", false, 1, LOWEST)); + spdy_util_.ConstructSpdyGet("https://www.example.org", false, 1, LOWEST)); scoped_ptr<SpdyFrame> host2_req( spdy_util_.ConstructSpdyGet("https://www.gmail.com", false, 3, LOWEST)); MockWrite spdy_writes[] = { @@ -11485,7 +11588,7 @@ TestCompletionCallback callback; HttpRequestInfo request1; request1.method = "GET"; - request1.url = GURL("https://www.google.com/"); + request1.url = GURL("https://www.example.org/"); request1.load_flags = 0; HttpNetworkTransaction trans1(DEFAULT_PRIORITY, session.get()); @@ -11595,7 +11698,7 @@ session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl); scoped_ptr<SpdyFrame> host1_req( - spdy_util_.ConstructSpdyGet("https://www.google.com", false, 1, LOWEST)); + spdy_util_.ConstructSpdyGet("https://www.example.org", false, 1, LOWEST)); scoped_ptr<SpdyFrame> host2_req( spdy_util_.ConstructSpdyGet("https://www.gmail.com", false, 3, LOWEST)); MockWrite spdy_writes[] = { @@ -11631,7 +11734,7 @@ TestCompletionCallback callback; HttpRequestInfo request1; request1.method = "GET"; - request1.url = GURL("https://www.google.com/"); + request1.url = GURL("https://www.example.org/"); request1.load_flags = 0; HttpNetworkTransaction trans1(DEFAULT_PRIORITY, session.get()); @@ -11684,8 +11787,8 @@ #undef MAYBE_UseIPConnectionPoolingWithHostCacheExpiration TEST_P(HttpNetworkTransactionTest, DoNotUseSpdySessionForHttp) { - const std::string https_url = "https://www.google.com:8080/"; - const std::string http_url = "http://www.google.com:8080/"; + const std::string https_url = "https://www.example.org:8080/"; + const std::string http_url = "http://www.example.org:8080/"; // SPDY GET for HTTPS URL scoped_ptr<SpdyFrame> req1( @@ -11713,7 +11816,7 @@ MockWrite writes2[] = { MockWrite(ASYNC, 4, "GET / HTTP/1.1\r\n" - "Host: www.google.com:8080\r\n" + "Host: www.example.org:8080\r\n" "Connection: keep-alive\r\n\r\n"), }; @@ -11765,11 +11868,11 @@ } TEST_P(HttpNetworkTransactionTest, DoNotUseSpdySessionForHttpOverTunnel) { - const std::string https_url = "https://www.google.com:8080/"; - const std::string http_url = "http://www.google.com:8080/"; + const std::string https_url = "https://www.example.org:8080/"; + const std::string http_url = "http://www.example.org:8080/"; // SPDY GET for HTTPS URL (through CONNECT tunnel) - const HostPortPair host_port_pair("www.google.com", 8080); + const HostPortPair host_port_pair("www.example.org", 8080); scoped_ptr<SpdyFrame> connect( spdy_util_.ConstructSpdyConnect(NULL, 0, 1, LOWEST, host_port_pair)); scoped_ptr<SpdyFrame> req1( @@ -11781,7 +11884,7 @@ SpdyHeaderBlock req2_block; req2_block[spdy_util_.GetMethodKey()] = "GET"; req2_block[spdy_util_.GetPathKey()] = "/"; - req2_block[spdy_util_.GetHostKey()] = "www.google.com:8080"; + req2_block[spdy_util_.GetHostKey()] = "www.example.org:8080"; req2_block[spdy_util_.GetSchemeKey()] = "http"; spdy_util_.MaybeAddVersionHeader(&req2_block); scoped_ptr<SpdyFrame> req2( @@ -11881,13 +11984,13 @@ // the certificate does not match the new origin. // http://crbug.com/134690 TEST_P(HttpNetworkTransactionTest, DoNotUseSpdySessionIfCertDoesNotMatch) { - const std::string url1 = "http://www.google.com/"; - const std::string url2 = "https://mail.google.com/"; + const std::string url1 = "http://www.example.org/"; + const std::string url2 = "https://news.example.org/"; const std::string ip_addr = "1.2.3.4"; // SPDY GET for HTTP URL (through SPDY proxy) scoped_ptr<SpdyHeaderBlock> headers( - spdy_util_.ConstructGetHeaderBlockForProxy("http://www.google.com/")); + spdy_util_.ConstructGetHeaderBlockForProxy("http://www.example.org/")); scoped_ptr<SpdyFrame> req1( spdy_util_.ConstructSpdySyn(1, *headers, LOWEST, false, true)); @@ -11941,18 +12044,14 @@ session_deps_.proxy_service.reset(new ProxyService( new ProxyConfigServiceFixed(proxy_config), nullptr, NULL)); + SSLSocketDataProvider ssl1(ASYNC, OK); // to the proxy + ssl1.SetNextProto(GetParam()); // Load a valid cert. Note, that this does not need to // be valid for proxy because the MockSSLClientSocket does // not actually verify it. But SpdySession will use this // to see if it is valid for the new origin - base::FilePath certs_dir = GetTestCertsDirectory(); - scoped_refptr<X509Certificate> server_cert( - ImportCertFromFile(certs_dir, "ok_cert.pem")); - ASSERT_NE(static_cast<X509Certificate*>(NULL), server_cert.get()); - - SSLSocketDataProvider ssl1(ASYNC, OK); // to the proxy - ssl1.SetNextProto(GetParam()); - ssl1.cert = server_cert; + ssl1.cert = ImportCertFromFile(GetTestCertsDirectory(), "ok_cert.pem"); + ASSERT_TRUE(ssl1.cert.get()); session_deps_.deterministic_socket_factory->AddSSLSocketDataProvider(&ssl1); session_deps_.deterministic_socket_factory->AddSocketDataProvider( data1.get()); @@ -11964,7 +12063,7 @@ data2.get()); session_deps_.host_resolver.reset(new MockCachingHostResolver()); - session_deps_.host_resolver->rules()->AddRule("mail.google.com", ip_addr); + session_deps_.host_resolver->rules()->AddRule("news.example.org", ip_addr); session_deps_.host_resolver->rules()->AddRule("proxy", ip_addr); scoped_refptr<HttpNetworkSession> session( @@ -12007,7 +12106,7 @@ // session. Verify that new url's from the same HttpNetworkSession (and a new // SpdySession) do work. http://crbug.com/224701 TEST_P(HttpNetworkTransactionTest, ErrorSocketNotConnected) { - const std::string https_url = "https://www.google.com/"; + const std::string https_url = "https://www.example.org/"; MockRead reads1[] = { MockRead(SYNCHRONOUS, ERR_CONNECTION_CLOSED, 0) @@ -12249,7 +12348,7 @@ TEST_P(HttpNetworkTransactionTest, HttpSyncConnectError) { HttpRequestInfo request; request.method = "GET"; - request.url = GURL("http://www.google.com/"); + request.url = GURL("http://www.example.org/"); request.load_flags = 0; scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps_)); @@ -12279,7 +12378,7 @@ TEST_P(HttpNetworkTransactionTest, HttpAsyncConnectError) { HttpRequestInfo request; request.method = "GET"; - request.url = GURL("http://www.google.com/"); + request.url = GURL("http://www.example.org/"); request.load_flags = 0; scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps_)); @@ -12309,7 +12408,7 @@ TEST_P(HttpNetworkTransactionTest, HttpSyncWriteError) { HttpRequestInfo request; request.method = "GET"; - request.url = GURL("http://www.google.com/"); + request.url = GURL("http://www.example.org/"); request.load_flags = 0; scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps_)); @@ -12345,7 +12444,7 @@ TEST_P(HttpNetworkTransactionTest, HttpAsyncWriteError) { HttpRequestInfo request; request.method = "GET"; - request.url = GURL("http://www.google.com/"); + request.url = GURL("http://www.example.org/"); request.load_flags = 0; scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps_)); @@ -12381,7 +12480,7 @@ TEST_P(HttpNetworkTransactionTest, HttpSyncReadError) { HttpRequestInfo request; request.method = "GET"; - request.url = GURL("http://www.google.com/"); + request.url = GURL("http://www.example.org/"); request.load_flags = 0; scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps_)); @@ -12389,9 +12488,10 @@ new HttpNetworkTransaction(DEFAULT_PRIORITY, session.get())); MockWrite data_writes[] = { - MockWrite("GET / HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Connection: keep-alive\r\n\r\n"), + MockWrite( + "GET / HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Connection: keep-alive\r\n\r\n"), }; MockRead data_reads[] = { MockRead(SYNCHRONOUS, ERR_CONNECTION_RESET), @@ -12419,7 +12519,7 @@ TEST_P(HttpNetworkTransactionTest, HttpAsyncReadError) { HttpRequestInfo request; request.method = "GET"; - request.url = GURL("http://www.google.com/"); + request.url = GURL("http://www.example.org/"); request.load_flags = 0; scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps_)); @@ -12427,9 +12527,10 @@ new HttpNetworkTransaction(DEFAULT_PRIORITY, session.get())); MockWrite data_writes[] = { - MockWrite("GET / HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Connection: keep-alive\r\n\r\n"), + MockWrite( + "GET / HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Connection: keep-alive\r\n\r\n"), }; MockRead data_reads[] = { MockRead(ASYNC, ERR_CONNECTION_RESET), @@ -12457,7 +12558,7 @@ TEST_P(HttpNetworkTransactionTest, GetFullRequestHeadersIncludesExtraHeader) { HttpRequestInfo request; request.method = "GET"; - request.url = GURL("http://www.google.com/"); + request.url = GURL("http://www.example.org/"); request.load_flags = 0; request.extra_headers.SetHeader("X-Foo", "bar"); @@ -12466,10 +12567,11 @@ new HttpNetworkTransaction(DEFAULT_PRIORITY, session.get())); MockWrite data_writes[] = { - MockWrite("GET / HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Connection: keep-alive\r\n" - "X-Foo: bar\r\n\r\n"), + MockWrite( + "GET / HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Connection: keep-alive\r\n" + "X-Foo: bar\r\n\r\n"), }; MockRead data_reads[] = { MockRead("HTTP/1.1 200 OK\r\n" @@ -12929,7 +13031,8 @@ // The same logic needs to be tested for both ws: and wss: schemes, but this // test is already parameterised on NextProto, so it uses a loop to verify // that the different schemes work. - std::string test_cases[] = {"ws://www.google.com/", "wss://www.google.com/"}; + std::string test_cases[] = {"ws://www.example.org/", + "wss://www.example.org/"}; for (size_t i = 0; i < arraysize(test_cases); ++i) { scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps_)); HttpNetworkSessionPeer peer(session); @@ -12970,12 +13073,13 @@ HttpRequestInfo ssl_request; ssl_request.method = "GET"; - ssl_request.url = GURL("https://www.google.com/"); + ssl_request.url = GURL("https://www.example.org/"); MockWrite ssl_writes[] = { - MockWrite("GET / HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Connection: keep-alive\r\n\r\n"), + MockWrite( + "GET / HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Connection: keep-alive\r\n\r\n"), }; MockRead ssl_reads[] = { MockRead("HTTP/1.1 200 OK\r\n"), @@ -12994,12 +13098,13 @@ HttpRequestInfo http_request; http_request.method = "GET"; - http_request.url = GURL("http://www.google.com/"); + http_request.url = GURL("http://www.example.org/"); MockWrite http_writes[] = { - MockWrite("GET / HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Connection: keep-alive\r\n\r\n"), + MockWrite( + "GET / HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Connection: keep-alive\r\n\r\n"), }; MockRead http_reads[] = { MockRead("HTTP/1.1 200 OK\r\n"), @@ -13075,12 +13180,13 @@ HttpRequestInfo http_request; http_request.method = "GET"; - http_request.url = GURL("http://www.google.com/"); + http_request.url = GURL("http://www.example.org/"); MockWrite http_writes[] = { - MockWrite("GET / HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Connection: keep-alive\r\n\r\n"), + MockWrite( + "GET / HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Connection: keep-alive\r\n\r\n"), }; MockRead http_reads[] = { MockRead("HTTP/1.1 200 OK\r\n"), @@ -13610,7 +13716,7 @@ TEST_P(HttpNetworkTransactionTest, ProxyHeadersNotSentOverWssTunnel) { HttpRequestInfo request; request.method = "GET"; - request.url = GURL("wss://www.google.com/"); + request.url = GURL("wss://www.example.org/"); AddWebSocketHeaders(&request.extra_headers); // Configure against proxy server "myproxy:70". @@ -13622,24 +13728,24 @@ // Since a proxy is configured, try to establish a tunnel. MockWrite data_writes[] = { MockWrite( - "CONNECT www.google.com:443 HTTP/1.1\r\n" - "Host: www.google.com\r\n" + "CONNECT www.example.org:443 HTTP/1.1\r\n" + "Host: www.example.org\r\n" "Proxy-Connection: keep-alive\r\n\r\n"), // After calling trans->RestartWithAuth(), this is the request we should // be issuing -- the final header line contains the credentials. MockWrite( - "CONNECT www.google.com:443 HTTP/1.1\r\n" - "Host: www.google.com\r\n" + "CONNECT www.example.org:443 HTTP/1.1\r\n" + "Host: www.example.org\r\n" "Proxy-Connection: keep-alive\r\n" "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n\r\n"), MockWrite( "GET / HTTP/1.1\r\n" - "Host: www.google.com\r\n" + "Host: www.example.org\r\n" "Connection: Upgrade\r\n" "Upgrade: websocket\r\n" - "Origin: http://www.google.com\r\n" + "Origin: http://www.example.org\r\n" "Sec-WebSocket-Version: 13\r\n" "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n\r\n"), }; @@ -13716,7 +13822,7 @@ TEST_P(HttpNetworkTransactionTest, ProxyHeadersNotSentOverWsTunnel) { HttpRequestInfo request; request.method = "GET"; - request.url = GURL("ws://www.google.com/"); + request.url = GURL("ws://www.example.org/"); AddWebSocketHeaders(&request.extra_headers); // Configure against proxy server "myproxy:70". @@ -13731,17 +13837,17 @@ // they cannot and will not use the same TCP/IP connection as the // preflight HTTP request. MockWrite( - "CONNECT www.google.com:80 HTTP/1.1\r\n" - "Host: www.google.com:80\r\n" + "CONNECT www.example.org:80 HTTP/1.1\r\n" + "Host: www.example.org:80\r\n" "Proxy-Connection: keep-alive\r\n" "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n\r\n"), MockWrite( "GET / HTTP/1.1\r\n" - "Host: www.google.com\r\n" + "Host: www.example.org\r\n" "Connection: Upgrade\r\n" "Upgrade: websocket\r\n" - "Origin: http://www.google.com\r\n" + "Origin: http://www.example.org\r\n" "Sec-WebSocket-Version: 13\r\n" "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n\r\n"), };
diff --git a/net/http/http_request_headers.cc b/net/http/http_request_headers.cc index f0b7cfb..eaeec0d 100644 --- a/net/http/http_request_headers.cc +++ b/net/http/http_request_headers.cc
@@ -190,14 +190,14 @@ base::Value* HttpRequestHeaders::NetLogCallback( const std::string* request_line, - NetLog::LogLevel log_level) const { + NetLogCaptureMode capture_mode) const { base::DictionaryValue* dict = new base::DictionaryValue(); dict->SetString("line", *request_line); base::ListValue* headers = new base::ListValue(); for (HeaderVector::const_iterator it = headers_.begin(); it != headers_.end(); ++it) { - std::string log_value = ElideHeaderValueForNetLog( - log_level, it->key, it->value); + std::string log_value = + ElideHeaderValueForNetLog(capture_mode, it->key, it->value); headers->Append(new base::StringValue( base::StringPrintf("%s: %s", it->key.c_str(), log_value.c_str())));
diff --git a/net/http/http_request_headers.h b/net/http/http_request_headers.h index d8d52a09..d0666de 100644 --- a/net/http/http_request_headers.h +++ b/net/http/http_request_headers.h
@@ -148,7 +148,7 @@ // Takes in the request line and returns a Value for use with the NetLog // containing both the request line and all headers fields. base::Value* NetLogCallback(const std::string* request_line, - NetLog::LogLevel log_level) const; + NetLogCaptureMode capture_mode) const; // Takes in a Value created by the above function, and attempts to extract the // request line and create a copy of the original headers. Returns true on
diff --git a/net/http/http_request_headers_unittest.cc b/net/http/http_request_headers_unittest.cc index d33b4731..7c00c6ee 100644 --- a/net/http/http_request_headers_unittest.cc +++ b/net/http/http_request_headers_unittest.cc
@@ -171,8 +171,8 @@ headers.SetHeader("A", "a"); std::string request_line("GET /stuff"); - scoped_ptr<base::Value> event_param( - headers.NetLogCallback(&request_line, NetLog::LOG_ALL_BUT_BYTES)); + scoped_ptr<base::Value> event_param(headers.NetLogCallback( + &request_line, NetLogCaptureMode::IncludeCookiesAndCredentials())); HttpRequestHeaders headers2; std::string request_line2;
diff --git a/net/http/http_response_headers.cc b/net/http/http_response_headers.cc index 3aef42a6..69a0aa8 100644 --- a/net/http/http_response_headers.cc +++ b/net/http/http_response_headers.cc
@@ -1392,7 +1392,7 @@ } base::Value* HttpResponseHeaders::NetLogCallback( - NetLog::LogLevel log_level) const { + NetLogCaptureMode capture_mode) const { base::DictionaryValue* dict = new base::DictionaryValue(); base::ListValue* headers = new base::ListValue(); headers->Append(new base::StringValue(GetStatusLine())); @@ -1400,7 +1400,8 @@ std::string name; std::string value; while (EnumerateHeaderLines(&iterator, &name, &value)) { - std::string log_value = ElideHeaderValueForNetLog(log_level, name, value); + std::string log_value = + ElideHeaderValueForNetLog(capture_mode, name, value); std::string escaped_name = EscapeNonASCII(name); std::string escaped_value = EscapeNonASCII(log_value); headers->Append(
diff --git a/net/http/http_response_headers.h b/net/http/http_response_headers.h index a87a5f5..dd3d9cb 100644 --- a/net/http/http_response_headers.h +++ b/net/http/http_response_headers.h
@@ -286,7 +286,7 @@ bool IsChunkEncoded() const; // Creates a Value for use with the NetLog containing the response headers. - base::Value* NetLogCallback(NetLog::LogLevel log_level) const; + base::Value* NetLogCallback(NetLogCaptureMode capture_mode) const; // Takes in a Value created by the above function, and attempts to create a // copy of the original headers. Returns true on success. On failure,
diff --git a/net/http/http_response_headers_unittest.cc b/net/http/http_response_headers_unittest.cc index 3557423..81bd5a3 100644 --- a/net/http/http_response_headers_unittest.cc +++ b/net/http/http_response_headers_unittest.cc
@@ -2266,8 +2266,8 @@ scoped_refptr<net::HttpResponseHeaders> parsed( new net::HttpResponseHeaders(headers)); - scoped_ptr<base::Value> event_param( - parsed->NetLogCallback(net::NetLog::LOG_ALL_BUT_BYTES)); + scoped_ptr<base::Value> event_param(parsed->NetLogCallback( + net::NetLogCaptureMode::IncludeCookiesAndCredentials())); scoped_refptr<net::HttpResponseHeaders> recreated; ASSERT_TRUE(net::HttpResponseHeaders::FromNetLogParam(event_param.get(),
diff --git a/net/http/http_server_properties_manager_unittest.cc b/net/http/http_server_properties_manager_unittest.cc index fb548bc1..55dc7b8 100644 --- a/net/http/http_server_properties_manager_unittest.cc +++ b/net/http/http_server_properties_manager_unittest.cc
@@ -40,15 +40,14 @@ InitializeOnNetworkThread(); } - virtual ~TestingHttpServerPropertiesManager() {} + ~TestingHttpServerPropertiesManager() override {} // Make these methods public for testing. using HttpServerPropertiesManager::ScheduleUpdateCacheOnPrefThread; using HttpServerPropertiesManager::ScheduleUpdatePrefsOnNetworkThread; // Post tasks without a delay during tests. - virtual void StartPrefsUpdateTimerOnNetworkThread( - base::TimeDelta delay) override { + void StartPrefsUpdateTimerOnNetworkThread(base::TimeDelta delay) override { HttpServerPropertiesManager::StartPrefsUpdateTimerOnNetworkThread( base::TimeDelta()); } @@ -58,8 +57,7 @@ } // Post tasks without a delay during tests. - virtual void StartCacheUpdateTimerOnPrefThread( - base::TimeDelta delay) override { + void StartCacheUpdateTimerOnPrefThread(base::TimeDelta delay) override { HttpServerPropertiesManager::StartCacheUpdateTimerOnPrefThread( base::TimeDelta()); }
diff --git a/net/http/http_stream_factory_impl_job.cc b/net/http/http_stream_factory_impl_job.cc index 4ce799e8..5843d4c 100644 --- a/net/http/http_stream_factory_impl_job.cc +++ b/net/http/http_stream_factory_impl_job.cc
@@ -49,7 +49,7 @@ const GURL* url, const AlternativeService* alternative_service, RequestPriority priority, - NetLog::LogLevel /* log_level */) { + NetLogCaptureMode /* capture_mode */) { base::DictionaryValue* dict = new base::DictionaryValue(); dict->SetString("original_url", original_url->GetOrigin().spec()); dict->SetString("url", url->GetOrigin().spec()); @@ -63,7 +63,7 @@ base::Value* NetLogHttpStreamProtoCallback( const SSLClientSocket::NextProtoStatus status, const std::string* proto, - NetLog::LogLevel /* log_level */) { + NetLogCaptureMode /* capture_mode */) { base::DictionaryValue* dict = new base::DictionaryValue(); dict->SetString("next_proto_status",
diff --git a/net/http/http_stream_factory_impl_unittest.cc b/net/http/http_stream_factory_impl_unittest.cc index 3145408..1c0b982 100644 --- a/net/http/http_stream_factory_impl_unittest.cc +++ b/net/http/http_stream_factory_impl_unittest.cc
@@ -320,51 +320,47 @@ return last_num_streams_; } - virtual int RequestSocket(const std::string& group_name, - const void* socket_params, - RequestPriority priority, - ClientSocketHandle* handle, - const CompletionCallback& callback, - const BoundNetLog& net_log) override { + int RequestSocket(const std::string& group_name, + const void* socket_params, + RequestPriority priority, + ClientSocketHandle* handle, + const CompletionCallback& callback, + const BoundNetLog& net_log) override { ADD_FAILURE(); return ERR_UNEXPECTED; } - virtual void RequestSockets(const std::string& group_name, - const void* socket_params, - int num_sockets, - const BoundNetLog& net_log) override { + void RequestSockets(const std::string& group_name, + const void* socket_params, + int num_sockets, + const BoundNetLog& net_log) override { last_num_streams_ = num_sockets; } - virtual void CancelRequest(const std::string& group_name, - ClientSocketHandle* handle) override { + void CancelRequest(const std::string& group_name, + ClientSocketHandle* handle) override { ADD_FAILURE(); } - virtual void ReleaseSocket(const std::string& group_name, - scoped_ptr<StreamSocket> socket, - int id) override { + void ReleaseSocket(const std::string& group_name, + scoped_ptr<StreamSocket> socket, + int id) override { ADD_FAILURE(); } - virtual void CloseIdleSockets() override { - ADD_FAILURE(); - } - virtual int IdleSocketCount() const override { + void CloseIdleSockets() override { ADD_FAILURE(); } + int IdleSocketCount() const override { ADD_FAILURE(); return 0; } - virtual int IdleSocketCountInGroup( - const std::string& group_name) const override { + int IdleSocketCountInGroup(const std::string& group_name) const override { ADD_FAILURE(); return 0; } - virtual LoadState GetLoadState( - const std::string& group_name, - const ClientSocketHandle* handle) const override { + LoadState GetLoadState(const std::string& group_name, + const ClientSocketHandle* handle) const override { ADD_FAILURE(); return LOAD_STATE_IDLE; } - virtual base::TimeDelta ConnectionTimeout() const override { + base::TimeDelta ConnectionTimeout() const override { return base::TimeDelta(); }
diff --git a/net/http/http_stream_parser.cc b/net/http/http_stream_parser.cc index 5aeafa15..30a63015 100644 --- a/net/http/http_stream_parser.cc +++ b/net/http/http_stream_parser.cc
@@ -74,10 +74,11 @@ return false; } -base::Value* NetLogSendRequestBodyCallback(uint64 length, - bool is_chunked, - bool did_merge, - NetLog::LogLevel /* log_level */) { +base::Value* NetLogSendRequestBodyCallback( + uint64 length, + bool is_chunked, + bool did_merge, + NetLogCaptureMode /* capture_mode */) { base::DictionaryValue* dict = new base::DictionaryValue(); dict->SetInteger("length", static_cast<int>(length)); dict->SetBoolean("is_chunked", is_chunked);
diff --git a/net/log/net_log.cc b/net/log/net_log.cc index 7f1d6e9..e112c2c 100644 --- a/net/log/net_log.cc +++ b/net/log/net_log.cc
@@ -17,22 +17,23 @@ namespace { -// Returns parameters for logging data transferred events. Includes number of -// bytes transferred and, if the log level indicates bytes should be logged and -// |byte_count| > 0, the bytes themselves. The bytes are hex-encoded, since -// base::StringValue only supports UTF-8. +// Returns parameters for logging data transferred events. At a minum includes +// the number of bytes transferred. If the capture mode allows logging byte +// contents and |byte_count| > 0, then will include the actual bytes. The +// bytes are hex-encoded, since base::StringValue only supports UTF-8. base::Value* BytesTransferredCallback(int byte_count, const char* bytes, - NetLog::LogLevel log_level) { + NetLogCaptureMode capture_mode) { base::DictionaryValue* dict = new base::DictionaryValue(); dict->SetInteger("byte_count", byte_count); - if (NetLog::IsLoggingBytes(log_level) && byte_count > 0) + if (capture_mode.include_socket_bytes() && byte_count > 0) dict->SetString("hex_encoded_bytes", base::HexEncode(bytes, byte_count)); return dict; } -base::Value* SourceEventParametersCallback(const NetLog::Source source, - NetLog::LogLevel /* log_level */) { +base::Value* SourceEventParametersCallback( + const NetLog::Source source, + NetLogCaptureMode /* capture_mode */) { if (!source.IsValid()) return NULL; base::DictionaryValue* event_params = new base::DictionaryValue(); @@ -42,7 +43,7 @@ base::Value* NetLogIntegerCallback(const char* name, int value, - NetLog::LogLevel /* log_level */) { + NetLogCaptureMode /* capture_mode */) { base::DictionaryValue* event_params = new base::DictionaryValue(); event_params->SetInteger(name, value); return event_params; @@ -50,7 +51,7 @@ base::Value* NetLogInt64Callback(const char* name, int64 value, - NetLog::LogLevel /* log_level */) { + NetLogCaptureMode /* capture_mode */) { base::DictionaryValue* event_params = new base::DictionaryValue(); event_params->SetString(name, base::Int64ToString(value)); return event_params; @@ -58,7 +59,7 @@ base::Value* NetLogStringCallback(const char* name, const std::string* value, - NetLog::LogLevel /* log_level */) { + NetLogCaptureMode /* capture_mode */) { base::DictionaryValue* event_params = new base::DictionaryValue(); event_params->SetString(name, *value); return event_params; @@ -66,7 +67,7 @@ base::Value* NetLogString16Callback(const char* name, const base::string16* value, - NetLog::LogLevel /* log_level */) { + NetLogCaptureMode /* capture_mode */) { base::DictionaryValue* event_params = new base::DictionaryValue(); event_params->SetString(name, *value); return event_params; @@ -137,7 +138,7 @@ // Set the event-specific parameters. if (data_->parameters_callback) { - base::Value* value = data_->parameters_callback->Run(log_level_); + base::Value* value = data_->parameters_callback->Run(capture_mode_); if (value) entry_dict->Set("params", value); } @@ -147,7 +148,7 @@ base::Value* NetLog::Entry::ParametersToValue() const { if (data_->parameters_callback) - return data_->parameters_callback->Run(log_level_); + return data_->parameters_callback->Run(capture_mode_); return NULL; } @@ -166,15 +167,14 @@ NetLog::EntryData::~EntryData() { } -NetLog::Entry::Entry(const EntryData* data, LogLevel log_level) - : data_(data), log_level_(log_level) { +NetLog::Entry::Entry(const EntryData* data, NetLogCaptureMode capture_mode) + : data_(data), capture_mode_(capture_mode) { } NetLog::Entry::~Entry() { } -NetLog::ThreadSafeObserver::ThreadSafeObserver() - : log_level_(LOG_NONE), net_log_(NULL) { +NetLog::ThreadSafeObserver::ThreadSafeObserver() : net_log_(NULL) { } NetLog::ThreadSafeObserver::~ThreadSafeObserver() { @@ -184,9 +184,9 @@ DCHECK(!net_log_); } -NetLog::LogLevel NetLog::ThreadSafeObserver::log_level() const { +NetLogCaptureMode NetLog::ThreadSafeObserver::capture_mode() const { DCHECK(net_log_); - return log_level_; + return capture_mode_; } NetLog* NetLog::ThreadSafeObserver::net_log() const { @@ -194,10 +194,13 @@ } void NetLog::ThreadSafeObserver::OnAddEntryData(const EntryData& entry_data) { - OnAddEntry(Entry(&entry_data, log_level())); + OnAddEntry(Entry(&entry_data, capture_mode())); } -NetLog::NetLog() : last_id_(0), effective_log_level_(LOG_NONE) { +NetLog::NetLog() + : last_id_(0), + effective_capture_mode_int32_( + NetLogCaptureMode::None().ToInternalValue()) { } NetLog::~NetLog() { @@ -219,35 +222,36 @@ return base::subtle::NoBarrier_AtomicIncrement(&last_id_, 1); } -NetLog::LogLevel NetLog::GetLogLevel() const { - base::subtle::Atomic32 log_level = - base::subtle::NoBarrier_Load(&effective_log_level_); - return static_cast<net::NetLog::LogLevel>(log_level); +NetLogCaptureMode NetLog::GetCaptureMode() const { + base::subtle::Atomic32 capture_mode = + base::subtle::NoBarrier_Load(&effective_capture_mode_int32_); + return NetLogCaptureMode::FromInternalValue(capture_mode); } void NetLog::DeprecatedAddObserver(net::NetLog::ThreadSafeObserver* observer, - LogLevel log_level) { - DCHECK_NE(LOG_NONE, log_level); + NetLogCaptureMode capture_mode) { + DCHECK(capture_mode.enabled()); + base::AutoLock lock(lock_); DCHECK(!observer->net_log_); - DCHECK_EQ(LOG_NONE, observer->log_level_); + DCHECK(!observer->capture_mode_.enabled()); observers_.AddObserver(observer); observer->net_log_ = this; - observer->log_level_ = log_level; - UpdateLogLevel(); + observer->capture_mode_ = capture_mode; + UpdateCaptureMode(); } -void NetLog::SetObserverLogLevel(net::NetLog::ThreadSafeObserver* observer, - LogLevel log_level) { - DCHECK_NE(LOG_NONE, log_level); +void NetLog::SetObserverCaptureMode(net::NetLog::ThreadSafeObserver* observer, + NetLogCaptureMode capture_mode) { + DCHECK(capture_mode.enabled()); base::AutoLock lock(lock_); DCHECK(observers_.HasObserver(observer)); DCHECK_EQ(this, observer->net_log_); - DCHECK_NE(LOG_NONE, observer->log_level_); - observer->log_level_ = log_level; - UpdateLogLevel(); + DCHECK(observer->capture_mode_.enabled()); + observer->capture_mode_ = capture_mode; + UpdateCaptureMode(); } void NetLog::DeprecatedRemoveObserver( @@ -256,26 +260,26 @@ DCHECK(observers_.HasObserver(observer)); DCHECK_EQ(this, observer->net_log_); - DCHECK_NE(LOG_NONE, observer->log_level_); + DCHECK(observer->capture_mode_.enabled()); observers_.RemoveObserver(observer); observer->net_log_ = NULL; - observer->log_level_ = LOG_NONE; - UpdateLogLevel(); + observer->capture_mode_ = NetLogCaptureMode(); + UpdateCaptureMode(); } -void NetLog::UpdateLogLevel() { +void NetLog::UpdateCaptureMode() { lock_.AssertAcquired(); - // Look through all the observers and find the finest granularity - // log level (higher values of the enum imply *lower* log levels). - LogLevel new_effective_log_level = LOG_NONE; + // Accumulate the capture mode of all the observers to find the maximum level. + NetLogCaptureMode new_capture_mode = NetLogCaptureMode::None(); ObserverListBase<ThreadSafeObserver>::Iterator it(&observers_); ThreadSafeObserver* observer; while ((observer = it.GetNext()) != NULL) { - new_effective_log_level = - std::min(new_effective_log_level, observer->log_level()); + new_capture_mode = + NetLogCaptureMode::Max(new_capture_mode, observer->capture_mode()); } - base::subtle::NoBarrier_Store(&effective_log_level_, new_effective_log_level); + base::subtle::NoBarrier_Store(&effective_capture_mode_int32_, + new_capture_mode.ToInternalValue()); } // static @@ -345,16 +349,6 @@ } // static -bool NetLog::IsLoggingBytes(LogLevel log_level) { - return log_level == NetLog::LOG_ALL; -} - -// static -bool NetLog::IsLogging(LogLevel log_level) { - return log_level < NetLog::LOG_NONE; -} - -// static NetLog::ParametersCallback NetLog::IntegerCallback(const char* name, int value) { return base::Bind(&NetLogIntegerCallback, name, value); @@ -384,7 +378,7 @@ const Source& source, EventPhase phase, const NetLog::ParametersCallback* parameters_callback) { - if (GetLogLevel() == LOG_NONE) + if (!GetCaptureMode().enabled()) return; EntryData entry_data(type, source, phase, base::TimeTicks::Now(), parameters_callback); @@ -474,20 +468,12 @@ AddEvent(event_type, base::Bind(BytesTransferredCallback, byte_count, bytes)); } -NetLog::LogLevel BoundNetLog::GetLogLevel() const { +NetLogCaptureMode BoundNetLog::GetCaptureMode() const { CrashIfInvalid(); if (net_log_) - return net_log_->GetLogLevel(); - return NetLog::LOG_NONE; -} - -bool BoundNetLog::IsLoggingBytes() const { - return NetLog::IsLoggingBytes(GetLogLevel()); -} - -bool BoundNetLog::IsLogging() const { - return NetLog::IsLogging(GetLogLevel()); + return net_log_->GetCaptureMode(); + return NetLogCaptureMode(); } // static
diff --git a/net/log/net_log.h b/net/log/net_log.h index dd22d5e..7d32e1c9 100644 --- a/net/log/net_log.h +++ b/net/log/net_log.h
@@ -18,6 +18,7 @@ #include "base/synchronization/lock.h" #include "base/time/time.h" #include "net/base/net_export.h" +#include "net/log/net_log_capture_mode.h" namespace base { class DictionaryValue; @@ -66,33 +67,11 @@ SOURCE_COUNT }; - // Specifies the granularity of events that should be emitted to the log. - // - // Since the LogLevel may be read and set on any thread without locking, it - // may be possible for an Observer to receive an event or parameters that - // normally wouldn't be logged at the currently active log level. - enum LogLevel { - // Log everything possible, even if it is slow and memory expensive. - // Includes logging of transferred bytes. - LOG_ALL, - - // Log all events, but do not include the actual transferred bytes as - // parameters for bytes sent/received events. - LOG_ALL_BUT_BYTES, - - // Log all events, but do not include the actual transferred bytes and - // remove cookies and HTTP credentials. - LOG_STRIP_PRIVATE_DATA, - - // Don't log any events. - LOG_NONE, - }; - // A callback function that return a Value representation of the parameters // associated with an event. If called, it will be called synchonously, // so it need not have owning references. May be called more than once, or // not at all. May return NULL. - typedef base::Callback<base::Value*(LogLevel)> ParametersCallback; + typedef base::Callback<base::Value*(NetLogCaptureMode)> ParametersCallback; // Identifies the entity that generated this log. The |id| field should // uniquely identify the source, and is used by log observers to infer @@ -137,12 +116,12 @@ const ParametersCallback* const parameters_callback; }; - // An Entry pre-binds EntryData to a LogLevel, so observers will observe the - // output of ToValue() and ParametersToValue() at their log level rather than - // current maximum. + // An Entry pre-binds EntryData to a capture mode, so observers will observe + // the output of ToValue() and ParametersToValue() at their log capture mode + // rather than the current maximum. class NET_EXPORT Entry { public: - Entry(const EntryData* data, LogLevel log_level); + Entry(const EntryData* data, NetLogCaptureMode capture_mode); ~Entry(); EventType type() const { return data_->type; } @@ -161,8 +140,8 @@ private: const EntryData* const data_; - // Log level when the event occurred. - const LogLevel log_level_; + // Log capture mode when the event occurred. + const NetLogCaptureMode capture_mode_; // It is not safe to copy this class, since |parameters_callback_| may // include pointers that become stale immediately after the event is added, @@ -185,9 +164,9 @@ // NetLog is destroyed. ThreadSafeObserver(); - // Returns the minimum log level for events this observer wants to + // Returns the capture mode for events this observer wants to // receive. Must not be called when not watching a NetLog. - LogLevel log_level() const; + NetLogCaptureMode capture_mode() const; // Returns the NetLog we are currently watching, if any. Returns NULL // otherwise. @@ -210,7 +189,7 @@ void OnAddEntryData(const EntryData& entry_data); // Both of these values are only modified by the NetLog. - LogLevel log_level_; + NetLogCaptureMode capture_mode_; NetLog* net_log_; DISALLOW_COPY_AND_ASSIGN(ThreadSafeObserver); @@ -228,11 +207,11 @@ // will be unique and greater than 0. uint32 NextID(); - // Returns the logging level for this NetLog. This is used to avoid computing + // Returns the capture mode for this NetLog. This is used to avoid computing // and saving expensive log entries. - LogLevel GetLogLevel() const; + NetLogCaptureMode GetCaptureMode() const; - // Adds an observer and sets its log level. The observer must not be + // Adds an observer and sets its log capture mode. The observer must not be // watching any NetLog, including this one, when this is called. // // NetLog implementations must call NetLog::OnAddObserver to update the @@ -241,12 +220,14 @@ // DEPRECATED: The ability to watch the netlog stream is being phased out // (crbug.com/472693) as it can be misused in production code. Please consult // with a net/log OWNER before introducing a new dependency on this. - void DeprecatedAddObserver(ThreadSafeObserver* observer, LogLevel log_level); + void DeprecatedAddObserver(ThreadSafeObserver* observer, + NetLogCaptureMode capture_mode); - // Sets the log level of |observer| to |log_level|. |observer| must be - // watching |this|. NetLog implementations must call - // NetLog::OnSetObserverLogLevel to update the observer's internal state. - void SetObserverLogLevel(ThreadSafeObserver* observer, LogLevel log_level); + // Sets the log capture mode of |observer| to |capture_mode|. |observer| must + // be watching |this|. NetLog implementations must call + // NetLog::OnSetObserverCaptureMode to update the observer's internal state. + void SetObserverCaptureMode(ThreadSafeObserver* observer, + NetLogCaptureMode capture_mode); // Removes an observer. NetLog implementations must call // NetLog::OnAddObserver to update the observer's internal state. @@ -280,14 +261,6 @@ // Returns a C-String symbolic name for |event_phase|. static const char* EventPhaseToString(EventPhase event_phase); - // Returns true if |log_level| indicates the actual bytes transferred should - // be logged. This is only the case when |log_level| is LOG_ALL. - static bool IsLoggingBytes(LogLevel log_level); - - // Returns true if |log_level| indicates that events should be logged. This is - // the case when |log_level| is anything other than LOG_NONE. - static bool IsLogging(LogLevel log_level); - // Creates a ParametersCallback that encapsulates a single integer. // Warning: |name| must remain valid for the life of the callback. // TODO(mmenke): Rename this to be consistent with Int64Callback. @@ -318,9 +291,9 @@ EventPhase phase, const NetLog::ParametersCallback* parameters_callback); - // Called whenever an observer is added or removed, or has its log level - // changed. Must have acquired |lock_| prior to calling. - void UpdateLogLevel(); + // Called whenever an observer is added or removed, or has its log + // capture mode changed. Must have acquired |lock_| prior to calling. + void UpdateCaptureMode(); // |lock_| protects access to |observers_|. base::Lock lock_; @@ -328,8 +301,10 @@ // Last assigned source ID. Incremented to get the next one. base::subtle::Atomic32 last_id_; - // The current log level. - base::subtle::Atomic32 effective_log_level_; + // The current capture mode. Note that the capture mode is stored as an + // integer rather than a NetLogCaptureMode so that it can be easily + // read/written without a lock using Atomic32. + base::subtle::Atomic32 effective_capture_mode_int32_; // |lock_| must be acquired whenever reading or writing to this. ObserverList<ThreadSafeObserver, true> observers_; @@ -384,13 +359,7 @@ int byte_count, const char* bytes) const; - NetLog::LogLevel GetLogLevel() const; - - // Shortcut for NetLog::IsLoggingBytes(this->GetLogLevel()). - bool IsLoggingBytes() const; - - // Shortcut for NetLog::IsLogging(this->GetLogLevel()). - bool IsLogging() const; + NetLogCaptureMode GetCaptureMode() const; // Helper to create a BoundNetLog given a NetLog and a SourceType. Takes care // of creating a unique source ID, and handles the case of NULL net_log.
diff --git a/net/log/net_log_capture_mode.cc b/net/log/net_log_capture_mode.cc new file mode 100644 index 0000000..501911e0 --- /dev/null +++ b/net/log/net_log_capture_mode.cc
@@ -0,0 +1,95 @@ +// 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 "net/log/net_log_capture_mode.h" + +#include <algorithm> + +namespace net { + +namespace { + +// Integer representation for the capture mode. The numeric value is depended on +// for method of NetLogCaptureMode, which expect that higher values imply more +// capabilities. +enum InternalValue { + // Don't log any events. + NONE, + + // Log all events, but do not include the actual transferred bytes and + // remove cookies and HTTP credentials. + DEFAULT, + + // Log all events, but do not include the actual transferred bytes as + // parameters for bytes sent/received events. + INCLUDE_COOKIES_AND_CREDENTIALS, + + // Log everything possible, even if it is slow and memory expensive. + // Includes logging of transferred bytes. + INCLUDE_SOCKET_BYTES, +}; + +} // namespace + +// Default constructor creates an empty capture mode. +NetLogCaptureMode::NetLogCaptureMode() : NetLogCaptureMode(NONE) { +} + +NetLogCaptureMode NetLogCaptureMode::None() { + return NetLogCaptureMode(NONE); +} + +NetLogCaptureMode NetLogCaptureMode::Default() { + return NetLogCaptureMode(DEFAULT); +} + +NetLogCaptureMode NetLogCaptureMode::IncludeCookiesAndCredentials() { + return NetLogCaptureMode(INCLUDE_COOKIES_AND_CREDENTIALS); +} + +NetLogCaptureMode NetLogCaptureMode::IncludeSocketBytes() { + return NetLogCaptureMode(INCLUDE_SOCKET_BYTES); +} + +NetLogCaptureMode NetLogCaptureMode::Max(NetLogCaptureMode mode1, + NetLogCaptureMode mode2) { + return NetLogCaptureMode(std::max(mode1.value_, mode2.value_)); +} + +bool NetLogCaptureMode::enabled() const { + return value_ != NONE; +} + +bool NetLogCaptureMode::include_cookies_and_credentials() const { + return value_ >= INCLUDE_COOKIES_AND_CREDENTIALS; +} + +bool NetLogCaptureMode::include_socket_bytes() const { + return value_ >= INCLUDE_SOCKET_BYTES; +} + +bool NetLogCaptureMode::operator==(NetLogCaptureMode mode) const { + return value_ == mode.value_; +} + +bool NetLogCaptureMode::operator!=(NetLogCaptureMode mode) const { + return !(*this == mode); +} + +int32_t NetLogCaptureMode::ToInternalValueForTesting() const { + return ToInternalValue(); +} + +NetLogCaptureMode::NetLogCaptureMode(uint32_t value) : value_(value) { +} + +NetLogCaptureMode NetLogCaptureMode::FromInternalValue(int32_t value) { + return NetLogCaptureMode(value); +} + +int32_t NetLogCaptureMode::ToInternalValue() const { + return value_; +} + +} // namespace net
diff --git a/net/log/net_log_capture_mode.h b/net/log/net_log_capture_mode.h new file mode 100644 index 0000000..e4f26a3 --- /dev/null +++ b/net/log/net_log_capture_mode.h
@@ -0,0 +1,87 @@ +// 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 NET_LOG_NET_LOG_CAPTURE_MODE_H_ +#define NET_LOG_NET_LOG_CAPTURE_MODE_H_ + +#include <stdint.h> +#include <string> + +#include "net/base/net_export.h" + +namespace net { + +// NetLogCaptureMode specifies the granularity of events that should be emitted +// to the log. It is a simple wrapper around an integer, so it should be passed +// to functions by value rather than by reference. +class NET_EXPORT NetLogCaptureMode { + public: + // NOTE: Default assignment and copy constructor are OK. + + // The default constructor creates an empty capture mode (equivalent to + // None()). + NetLogCaptureMode(); + + // Constructs a capture mode which logs NOTHING. + // enabled() --> false + // include_cookies_and_credentials() --> false + // include_socket_bytes() --> false + static NetLogCaptureMode None(); + + // Constructs a capture mode which logs basic events and event parameters. + // enabled() --> true + // include_cookies_and_credentials() --> false + // include_socket_bytes() --> false + static NetLogCaptureMode Default(); + + // Constructs a capture mode which logs basic events, and additionally makes + // no effort to strip cookies and credentials. + // enabled() --> true + // include_cookies_and_credentials() --> true + // include_socket_bytes() --> false + static NetLogCaptureMode IncludeCookiesAndCredentials(); + + // Constructs a capture mode which logs the data sent/received from sockets. + // enabled() --> true + // include_cookies_and_credentials() --> true + // include_socket_bytes() --> true + static NetLogCaptureMode IncludeSocketBytes(); + + // Returns a capture mode that contains the maximal set of capabilities + // between |mode1| and |mode2|. + static NetLogCaptureMode Max(NetLogCaptureMode mode1, + NetLogCaptureMode mode2); + + // If enabled() is true, then _something_ is being captured. + bool enabled() const; + + // If include_cookies_and_credentials() is true , then it is OK to log + // events which contain cookies, credentials or other privacy sensitive data. + bool include_cookies_and_credentials() const; + + // If include_socket_bytes() is true, then it is OK to output the actual + // bytes read/written from the network, even if it contains private data. + bool include_socket_bytes() const; + + bool operator==(NetLogCaptureMode mode) const; + bool operator!=(NetLogCaptureMode mode) const; + + int32_t ToInternalValueForTesting() const; + + private: + // NetLog relies on the internal value of NetLogCaptureMode being an integer, + // so it can be read/written atomically across thread. + friend class NetLog; + + explicit NetLogCaptureMode(uint32_t value); + + static NetLogCaptureMode FromInternalValue(int32_t value); + int32_t ToInternalValue() const; + + int32_t value_; +}; + +} // namespace net + +#endif // NET_LOG_NET_LOG_CAPTURE_MODE_H_
diff --git a/net/log/net_log_capture_mode_unittest.cc b/net/log/net_log_capture_mode_unittest.cc new file mode 100644 index 0000000..b32beb9 --- /dev/null +++ b/net/log/net_log_capture_mode_unittest.cc
@@ -0,0 +1,93 @@ +// 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 "net/log/net_log_capture_mode.h" + +#include "testing/gtest/include/gtest/gtest.h" + +namespace net { + +namespace { + +TEST(NetLogCaptureMode, None) { + NetLogCaptureMode mode = NetLogCaptureMode::None(); + + EXPECT_FALSE(mode.enabled()); + EXPECT_FALSE(mode.include_cookies_and_credentials()); + EXPECT_FALSE(mode.include_socket_bytes()); + + EXPECT_EQ(mode, NetLogCaptureMode::None()); + EXPECT_NE(mode, NetLogCaptureMode::Default()); + EXPECT_NE(mode, NetLogCaptureMode::IncludeCookiesAndCredentials()); + EXPECT_NE(mode, NetLogCaptureMode::IncludeSocketBytes()); + EXPECT_EQ(mode.ToInternalValueForTesting(), + NetLogCaptureMode::None().ToInternalValueForTesting()); +} + +TEST(NetLogCaptureMode, Default) { + NetLogCaptureMode mode = NetLogCaptureMode::Default(); + + EXPECT_TRUE(mode.enabled()); + EXPECT_FALSE(mode.include_cookies_and_credentials()); + EXPECT_FALSE(mode.include_socket_bytes()); + + EXPECT_NE(mode, NetLogCaptureMode::None()); + EXPECT_EQ(mode, NetLogCaptureMode::Default()); + EXPECT_NE(mode, NetLogCaptureMode::IncludeCookiesAndCredentials()); + EXPECT_NE(mode, NetLogCaptureMode::IncludeSocketBytes()); + EXPECT_EQ(mode.ToInternalValueForTesting(), + NetLogCaptureMode::Default().ToInternalValueForTesting()); +} + +TEST(NetLogCaptureMode, IncludeCookiesAndCredentials) { + NetLogCaptureMode mode = NetLogCaptureMode::IncludeCookiesAndCredentials(); + + EXPECT_TRUE(mode.enabled()); + EXPECT_TRUE(mode.include_cookies_and_credentials()); + EXPECT_FALSE(mode.include_socket_bytes()); + + EXPECT_NE(mode, NetLogCaptureMode::None()); + EXPECT_NE(mode, NetLogCaptureMode::Default()); + EXPECT_EQ(mode, NetLogCaptureMode::IncludeCookiesAndCredentials()); + EXPECT_NE(mode, NetLogCaptureMode::IncludeSocketBytes()); + EXPECT_EQ(mode.ToInternalValueForTesting(), + NetLogCaptureMode::IncludeCookiesAndCredentials() + .ToInternalValueForTesting()); +} + +TEST(NetLogCaptureMode, IncludeSocketBytes) { + NetLogCaptureMode mode = NetLogCaptureMode::IncludeSocketBytes(); + + EXPECT_TRUE(mode.enabled()); + EXPECT_TRUE(mode.include_cookies_and_credentials()); + EXPECT_TRUE(mode.include_socket_bytes()); + + EXPECT_NE(mode, NetLogCaptureMode::None()); + EXPECT_NE(mode, NetLogCaptureMode::Default()); + EXPECT_NE(mode, NetLogCaptureMode::IncludeCookiesAndCredentials()); + EXPECT_EQ(mode, NetLogCaptureMode::IncludeSocketBytes()); + EXPECT_EQ( + mode.ToInternalValueForTesting(), + NetLogCaptureMode::IncludeSocketBytes().ToInternalValueForTesting()); +} + +TEST(NetLogCaptureMode, Max) { + NetLogCaptureMode none = NetLogCaptureMode::None(); + NetLogCaptureMode all = NetLogCaptureMode::IncludeSocketBytes(); + NetLogCaptureMode cookies = NetLogCaptureMode::IncludeCookiesAndCredentials(); + NetLogCaptureMode def = NetLogCaptureMode::Default(); + + EXPECT_EQ(all, NetLogCaptureMode::Max(none, all)); + EXPECT_EQ(all, NetLogCaptureMode::Max(all, none)); + + EXPECT_EQ(cookies, NetLogCaptureMode::Max(def, cookies)); + EXPECT_EQ(cookies, NetLogCaptureMode::Max(cookies, def)); + + EXPECT_EQ(all, NetLogCaptureMode::Max(def, all)); + EXPECT_EQ(all, NetLogCaptureMode::Max(all, def)); +} + +} // namespace + +} // namespace net
diff --git a/net/log/net_log_unittest.cc b/net/log/net_log_unittest.cc index 73e4e30..9fad152 100644 --- a/net/log/net_log_unittest.cc +++ b/net/log/net_log_unittest.cc
@@ -18,9 +18,13 @@ const int kThreads = 10; const int kEvents = 100; -base::Value* NetLogLevelCallback(NetLog::LogLevel log_level) { +base::Value* CaptureModeToValue(NetLogCaptureMode capture_mode) { + return new base::FundamentalValue(capture_mode.ToInternalValueForTesting()); +} + +base::Value* NetCaptureModeCallback(NetLogCaptureMode capture_mode) { base::DictionaryValue* dict = new base::DictionaryValue(); - dict->SetInteger("log_level", log_level); + dict->Set("capture_mode", CaptureModeToValue(capture_mode)); return dict; } @@ -42,16 +46,22 @@ EXPECT_FALSE(entries[0].params); } -// Check that the correct LogLevel is sent to NetLog Value callbacks. -TEST(NetLogTest, LogLevels) { +// Check that the correct CaptureMode is sent to NetLog Value callbacks. +TEST(NetLogTest, CaptureModes) { + NetLogCaptureMode kModes[] = { + NetLogCaptureMode::Default(), + NetLogCaptureMode::IncludeCookiesAndCredentials(), + NetLogCaptureMode::IncludeSocketBytes(), + }; + TestNetLog net_log; - for (int log_level = NetLog::LOG_ALL; log_level < NetLog::LOG_NONE; - ++log_level) { - net_log.SetLogLevel(static_cast<NetLog::LogLevel>(log_level)); - EXPECT_EQ(log_level, net_log.GetLogLevel()); + + for (NetLogCaptureMode mode : kModes) { + net_log.SetCaptureMode(mode); + EXPECT_EQ(mode, net_log.GetCaptureMode()); net_log.AddGlobalEntry(NetLog::TYPE_SOCKET_ALIVE, - base::Bind(NetLogLevelCallback)); + base::Bind(NetCaptureModeCallback)); TestNetLog::CapturedEntryList entries; net_log.GetEntries(&entries); @@ -63,9 +73,10 @@ EXPECT_EQ(NetLog::PHASE_NONE, entries[0].phase); EXPECT_GE(base::TimeTicks::Now(), entries[0].time); - int logged_log_level; - ASSERT_TRUE(entries[0].GetIntegerValue("log_level", &logged_log_level)); - EXPECT_EQ(log_level, logged_log_level); + int logged_capture_mode; + ASSERT_TRUE( + entries[0].GetIntegerValue("capture_mode", &logged_capture_mode)); + EXPECT_EQ(mode.ToInternalValueForTesting(), logged_capture_mode); net_log.Clear(); } @@ -111,12 +122,9 @@ ScopedVector<base::DictionaryValue> values_; }; -base::Value* LogLevelToValue(NetLog::LogLevel log_level) { - return new base::FundamentalValue(log_level); -} - void AddEvent(NetLog* net_log) { - net_log->AddGlobalEntry(NetLog::TYPE_CANCELLED, base::Bind(LogLevelToValue)); + net_log->AddGlobalEntry(NetLog::TYPE_CANCELLED, + base::Bind(CaptureModeToValue)); } // A thread that waits until an event has been signalled before calling @@ -180,15 +188,17 @@ for (int i = 0; i < kEvents; ++i) { ASSERT_FALSE(observer_.net_log()); - net_log_->DeprecatedAddObserver(&observer_, NetLog::LOG_ALL_BUT_BYTES); + net_log_->DeprecatedAddObserver( + &observer_, NetLogCaptureMode::IncludeCookiesAndCredentials()); ASSERT_EQ(net_log_, observer_.net_log()); - ASSERT_EQ(NetLog::LOG_ALL_BUT_BYTES, observer_.log_level()); - ASSERT_LE(net_log_->GetLogLevel(), NetLog::LOG_ALL_BUT_BYTES); + ASSERT_EQ(NetLogCaptureMode::IncludeCookiesAndCredentials(), + observer_.capture_mode()); - net_log_->SetObserverLogLevel(&observer_, NetLog::LOG_ALL); + net_log_->SetObserverCaptureMode(&observer_, + NetLogCaptureMode::IncludeSocketBytes()); ASSERT_EQ(net_log_, observer_.net_log()); - ASSERT_EQ(NetLog::LOG_ALL, observer_.log_level()); - ASSERT_LE(net_log_->GetLogLevel(), NetLog::LOG_ALL); + ASSERT_EQ(NetLogCaptureMode::IncludeSocketBytes(), + observer_.capture_mode()); net_log_->DeprecatedRemoveObserver(&observer_); ASSERT_TRUE(!observer_.net_log()); @@ -225,8 +235,10 @@ // Attach some observers. Since they're created after |net_log|, they'll // safely detach themselves on destruction. CountingObserver observers[3]; - for (size_t i = 0; i < arraysize(observers); ++i) - net_log.DeprecatedAddObserver(&observers[i], NetLog::LOG_ALL); + for (size_t i = 0; i < arraysize(observers); ++i) { + net_log.DeprecatedAddObserver(&observers[i], + NetLogCaptureMode::IncludeSocketBytes()); + } // Run a bunch of threads to completion, each of which will emit events to // |net_log|. @@ -246,22 +258,26 @@ AddEvent(&net_log); EXPECT_EQ(0, observer.count()); EXPECT_EQ(NULL, observer.net_log()); - EXPECT_EQ(NetLog::LOG_NONE, net_log.GetLogLevel()); + EXPECT_FALSE(net_log.GetCaptureMode().enabled()); // Add the observer and add an event. - net_log.DeprecatedAddObserver(&observer, NetLog::LOG_ALL_BUT_BYTES); + net_log.DeprecatedAddObserver( + &observer, NetLogCaptureMode::IncludeCookiesAndCredentials()); EXPECT_EQ(&net_log, observer.net_log()); - EXPECT_EQ(NetLog::LOG_ALL_BUT_BYTES, observer.log_level()); - EXPECT_EQ(NetLog::LOG_ALL_BUT_BYTES, net_log.GetLogLevel()); + EXPECT_EQ(NetLogCaptureMode::IncludeCookiesAndCredentials(), + observer.capture_mode()); + EXPECT_EQ(NetLogCaptureMode::IncludeCookiesAndCredentials(), + net_log.GetCaptureMode()); AddEvent(&net_log); EXPECT_EQ(1, observer.count()); // Change the observer's logging level and add an event. - net_log.SetObserverLogLevel(&observer, NetLog::LOG_ALL); + net_log.SetObserverCaptureMode(&observer, + NetLogCaptureMode::IncludeSocketBytes()); EXPECT_EQ(&net_log, observer.net_log()); - EXPECT_EQ(NetLog::LOG_ALL, observer.log_level()); - EXPECT_EQ(NetLog::LOG_ALL, net_log.GetLogLevel()); + EXPECT_EQ(NetLogCaptureMode::IncludeSocketBytes(), observer.capture_mode()); + EXPECT_EQ(NetLogCaptureMode::IncludeSocketBytes(), net_log.GetCaptureMode()); AddEvent(&net_log); EXPECT_EQ(2, observer.count()); @@ -269,16 +285,17 @@ // Remove observer and add an event. net_log.DeprecatedRemoveObserver(&observer); EXPECT_EQ(NULL, observer.net_log()); - EXPECT_EQ(NetLog::LOG_NONE, net_log.GetLogLevel()); + EXPECT_FALSE(net_log.GetCaptureMode().enabled()); AddEvent(&net_log); EXPECT_EQ(2, observer.count()); // Add the observer a final time, and add an event. - net_log.DeprecatedAddObserver(&observer, NetLog::LOG_ALL); + net_log.DeprecatedAddObserver(&observer, + NetLogCaptureMode::IncludeSocketBytes()); EXPECT_EQ(&net_log, observer.net_log()); - EXPECT_EQ(NetLog::LOG_ALL, observer.log_level()); - EXPECT_EQ(NetLog::LOG_ALL, net_log.GetLogLevel()); + EXPECT_EQ(NetLogCaptureMode::IncludeSocketBytes(), observer.capture_mode()); + EXPECT_EQ(NetLogCaptureMode::IncludeSocketBytes(), net_log.GetCaptureMode()); AddEvent(&net_log); EXPECT_EQ(3, observer.count()); @@ -290,19 +307,25 @@ LoggingObserver observer[2]; // Add first observer. - net_log.DeprecatedAddObserver(&observer[0], NetLog::LOG_ALL_BUT_BYTES); + net_log.DeprecatedAddObserver( + &observer[0], NetLogCaptureMode::IncludeCookiesAndCredentials()); EXPECT_EQ(&net_log, observer[0].net_log()); EXPECT_EQ(NULL, observer[1].net_log()); - EXPECT_EQ(NetLog::LOG_ALL_BUT_BYTES, observer[0].log_level()); - EXPECT_EQ(NetLog::LOG_ALL_BUT_BYTES, net_log.GetLogLevel()); + EXPECT_EQ(NetLogCaptureMode::IncludeCookiesAndCredentials(), + observer[0].capture_mode()); + EXPECT_EQ(NetLogCaptureMode::IncludeCookiesAndCredentials(), + net_log.GetCaptureMode()); // Add second observer observer. - net_log.DeprecatedAddObserver(&observer[1], NetLog::LOG_ALL); + net_log.DeprecatedAddObserver(&observer[1], + NetLogCaptureMode::IncludeSocketBytes()); EXPECT_EQ(&net_log, observer[0].net_log()); EXPECT_EQ(&net_log, observer[1].net_log()); - EXPECT_EQ(NetLog::LOG_ALL_BUT_BYTES, observer[0].log_level()); - EXPECT_EQ(NetLog::LOG_ALL, observer[1].log_level()); - EXPECT_EQ(NetLog::LOG_ALL, net_log.GetLogLevel()); + EXPECT_EQ(NetLogCaptureMode::IncludeCookiesAndCredentials(), + observer[0].capture_mode()); + EXPECT_EQ(NetLogCaptureMode::IncludeSocketBytes(), + observer[1].capture_mode()); + EXPECT_EQ(NetLogCaptureMode::IncludeSocketBytes(), net_log.GetCaptureMode()); // Add event and make sure both observers receive it at their respective log // levels. @@ -310,17 +333,19 @@ AddEvent(&net_log); ASSERT_EQ(1U, observer[0].GetNumValues()); ASSERT_TRUE(observer[0].GetValue(0)->GetInteger("params", ¶m)); - EXPECT_EQ(observer[0].log_level(), param); + EXPECT_EQ(observer[0].capture_mode().ToInternalValueForTesting(), param); ASSERT_EQ(1U, observer[1].GetNumValues()); ASSERT_TRUE(observer[1].GetValue(0)->GetInteger("params", ¶m)); - EXPECT_EQ(observer[1].log_level(), param); + EXPECT_EQ(observer[1].capture_mode().ToInternalValueForTesting(), param); // Remove second observer. net_log.DeprecatedRemoveObserver(&observer[1]); EXPECT_EQ(&net_log, observer[0].net_log()); EXPECT_EQ(NULL, observer[1].net_log()); - EXPECT_EQ(NetLog::LOG_ALL_BUT_BYTES, observer[0].log_level()); - EXPECT_EQ(NetLog::LOG_ALL_BUT_BYTES, net_log.GetLogLevel()); + EXPECT_EQ(NetLogCaptureMode::IncludeCookiesAndCredentials(), + observer[0].capture_mode()); + EXPECT_EQ(NetLogCaptureMode::IncludeCookiesAndCredentials(), + net_log.GetCaptureMode()); // Add event and make sure only second observer gets it. AddEvent(&net_log); @@ -331,7 +356,7 @@ net_log.DeprecatedRemoveObserver(&observer[0]); EXPECT_EQ(NULL, observer[0].net_log()); EXPECT_EQ(NULL, observer[1].net_log()); - EXPECT_EQ(NetLog::LOG_NONE, net_log.GetLogLevel()); + EXPECT_FALSE(net_log.GetCaptureMode().enabled()); // Add event and make sure neither observer gets it. AddEvent(&net_log);
diff --git a/net/log/net_log_util.cc b/net/log/net_log_util.cc index 3ce6f624..d178a32 100644 --- a/net/log/net_log_util.cc +++ b/net/log/net_log_util.cc
@@ -129,7 +129,7 @@ // Returns a Value representing the state of a pre-existing URLRequest when // net-internals was opened. base::Value* GetRequestStateAsValue(const net::URLRequest* request, - net::NetLog::LogLevel log_level) { + net::NetLogCaptureMode capture_mode) { return request->GetStateAsValue(); } @@ -255,18 +255,10 @@ // their symbolic names. constants_dict->Set("logSourceType", net::NetLog::GetSourceTypesAsValue()); - // Information about the relationship between LogLevel enums and their - // symbolic names. - { - base::DictionaryValue* dict = new base::DictionaryValue(); - - dict->SetInteger("LOG_ALL", net::NetLog::LOG_ALL); - dict->SetInteger("LOG_ALL_BUT_BYTES", net::NetLog::LOG_ALL_BUT_BYTES); - dict->SetInteger("LOG_STRIP_PRIVATE_DATA", - net::NetLog::LOG_STRIP_PRIVATE_DATA); - - constants_dict->Set("logLevelType", dict); - } + // TODO(eroman): This is here for compatibility in loading new log files with + // older builds of Chrome. Safe to remove this once M45 is on the stable + // channel. + constants_dict->Set("logLevelType", new base::DictionaryValue()); // Information about the relationship between address family enums and // their symbolic names. @@ -538,7 +530,7 @@ net::NetLog::EntryData entry_data( net::NetLog::TYPE_REQUEST_ALIVE, request->net_log().source(), net::NetLog::PHASE_BEGIN, request->creation_time(), &callback); - NetLog::Entry entry(&entry_data, request->net_log().GetLogLevel()); + NetLog::Entry entry(&entry_data, request->net_log().GetCaptureMode()); observer->OnAddEntry(entry); } }
diff --git a/net/log/test_net_log.cc b/net/log/test_net_log.cc index 695eacc..d98ffc3a 100644 --- a/net/log/test_net_log.cc +++ b/net/log/test_net_log.cc
@@ -7,15 +7,16 @@ namespace net { TestNetLog::TestNetLog() { - DeprecatedAddObserver(&capturing_net_log_observer_, LOG_ALL_BUT_BYTES); + DeprecatedAddObserver(&capturing_net_log_observer_, + NetLogCaptureMode::IncludeCookiesAndCredentials()); } TestNetLog::~TestNetLog() { DeprecatedRemoveObserver(&capturing_net_log_observer_); } -void TestNetLog::SetLogLevel(NetLog::LogLevel log_level) { - SetObserverLogLevel(&capturing_net_log_observer_, log_level); +void TestNetLog::SetCaptureMode(NetLogCaptureMode capture_mode) { + SetObserverCaptureMode(&capturing_net_log_observer_, capture_mode); } void TestNetLog::GetEntries(TestNetLog::CapturedEntryList* entry_list) const { @@ -62,8 +63,8 @@ capturing_net_log_.Clear(); } -void BoundTestNetLog::SetLogLevel(NetLog::LogLevel log_level) { - capturing_net_log_.SetLogLevel(log_level); +void BoundTestNetLog::SetCaptureMode(NetLogCaptureMode capture_mode) { + capturing_net_log_.SetCaptureMode(capture_mode); } } // namespace net
diff --git a/net/log/test_net_log.h b/net/log/test_net_log.h index 174f715..1526d827 100644 --- a/net/log/test_net_log.h +++ b/net/log/test_net_log.h
@@ -28,7 +28,7 @@ TestNetLog(); ~TestNetLog() override; - void SetLogLevel(LogLevel log_level); + void SetCaptureMode(NetLogCaptureMode capture_mode); // Below methods are forwarded to capturing_net_log_observer_. void GetEntries(CapturedEntryList* entry_list) const; @@ -67,8 +67,8 @@ void Clear(); - // Sets the log level of the underlying TestNetLog. - void SetLogLevel(NetLog::LogLevel log_level); + // Sets the capture mode of the underlying TestNetLog. + void SetCaptureMode(NetLogCaptureMode capture_mode); private: TestNetLog capturing_net_log_;
diff --git a/net/log/trace_net_log_observer.cc b/net/log/trace_net_log_observer.cc index a283ea5..2ce8bc0 100644 --- a/net/log/trace_net_log_observer.cc +++ b/net/log/trace_net_log_observer.cc
@@ -100,8 +100,7 @@ } void TraceNetLogObserver::OnTraceLogEnabled() { - net_log_to_watch_->DeprecatedAddObserver(this, - NetLog::LOG_STRIP_PRIVATE_DATA); + net_log_to_watch_->DeprecatedAddObserver(this, NetLogCaptureMode::Default()); } void TraceNetLogObserver::OnTraceLogDisabled() {
diff --git a/net/log/write_to_file_net_log_observer.cc b/net/log/write_to_file_net_log_observer.cc index 668b866..9df765eb 100644 --- a/net/log/write_to_file_net_log_observer.cc +++ b/net/log/write_to_file_net_log_observer.cc
@@ -18,15 +18,16 @@ namespace net { WriteToFileNetLogObserver::WriteToFileNetLogObserver() - : log_level_(NetLog::LOG_STRIP_PRIVATE_DATA), added_events_(false) { + : capture_mode_(NetLogCaptureMode::Default()), added_events_(false) { } WriteToFileNetLogObserver::~WriteToFileNetLogObserver() { } -void WriteToFileNetLogObserver::set_log_level(net::NetLog::LogLevel log_level) { +void WriteToFileNetLogObserver::set_capture_mode( + net::NetLogCaptureMode capture_mode) { DCHECK(!net_log()); - log_level_ = log_level; + capture_mode_ = capture_mode; } void WriteToFileNetLogObserver::StartObserving( @@ -62,7 +63,7 @@ CreateNetLogEntriesForActiveObjects(contexts, this); } - net_log->DeprecatedAddObserver(this, log_level_); + net_log->DeprecatedAddObserver(this, capture_mode_); } void WriteToFileNetLogObserver::StopObserving(
diff --git a/net/log/write_to_file_net_log_observer.h b/net/log/write_to_file_net_log_observer.h index bd147e3..8ef0115 100644 --- a/net/log/write_to_file_net_log_observer.h +++ b/net/log/write_to_file_net_log_observer.h
@@ -31,8 +31,8 @@ WriteToFileNetLogObserver(); ~WriteToFileNetLogObserver() override; - // Sets the log level to log at. Must be called before StartObserving. - void set_log_level(NetLog::LogLevel log_level); + // Sets the capture mode to log at. Must be called before StartObserving. + void set_capture_mode(NetLogCaptureMode capture_mode); // Starts observing |net_log| and writes output to |file|. Must not already // be watching a NetLog. Separate from constructor to enforce thread safety. @@ -65,8 +65,8 @@ private: base::ScopedFILE file_; - // The LogLevel to log at. - NetLog::LogLevel log_level_; + // The capture mode to log at. + NetLogCaptureMode capture_mode_; // True if OnAddEntry() has been called at least once. bool added_events_;
diff --git a/net/log/write_to_file_net_log_observer_unittest.cc b/net/log/write_to_file_net_log_observer_unittest.cc index 4c12cf3d..4cabfe8c 100644 --- a/net/log/write_to_file_net_log_observer_unittest.cc +++ b/net/log/write_to_file_net_log_observer_unittest.cc
@@ -61,21 +61,23 @@ ASSERT_TRUE(dict->GetDictionary("constants", &constants)); } -TEST_F(WriteToFileNetLogObserverTest, LogLevel) { +TEST_F(WriteToFileNetLogObserverTest, CaptureMode) { base::ScopedFILE file(base::OpenFile(log_path_, "w")); ASSERT_TRUE(file); WriteToFileNetLogObserver logger; logger.StartObserving(&net_log_, file.Pass(), nullptr, nullptr); - EXPECT_EQ(NetLog::LOG_STRIP_PRIVATE_DATA, logger.log_level()); - EXPECT_EQ(NetLog::LOG_STRIP_PRIVATE_DATA, net_log_.GetLogLevel()); + EXPECT_EQ(NetLogCaptureMode::Default(), logger.capture_mode()); + EXPECT_EQ(NetLogCaptureMode::Default(), net_log_.GetCaptureMode()); logger.StopObserving(nullptr); file.reset(base::OpenFile(log_path_, "w")); ASSERT_TRUE(file); - logger.set_log_level(NetLog::LOG_ALL_BUT_BYTES); + logger.set_capture_mode(NetLogCaptureMode::IncludeCookiesAndCredentials()); logger.StartObserving(&net_log_, file.Pass(), nullptr, nullptr); - EXPECT_EQ(NetLog::LOG_ALL_BUT_BYTES, logger.log_level()); - EXPECT_EQ(NetLog::LOG_ALL_BUT_BYTES, net_log_.GetLogLevel()); + EXPECT_EQ(NetLogCaptureMode::IncludeCookiesAndCredentials(), + logger.capture_mode()); + EXPECT_EQ(NetLogCaptureMode::IncludeCookiesAndCredentials(), + net_log_.GetCaptureMode()); logger.StopObserving(nullptr); } @@ -90,7 +92,7 @@ NetLog::EntryData entry_data(NetLog::TYPE_PROXY_SERVICE, source, NetLog::PHASE_BEGIN, base::TimeTicks::Now(), NULL); - NetLog::Entry entry(&entry_data, NetLog::LOG_ALL); + NetLog::Entry entry(&entry_data, NetLogCaptureMode::IncludeSocketBytes()); logger->OnAddEntry(entry); logger->StopObserving(nullptr); logger.reset(); @@ -120,7 +122,7 @@ NetLog::EntryData entry_data(NetLog::TYPE_PROXY_SERVICE, source, NetLog::PHASE_BEGIN, base::TimeTicks::Now(), NULL); - NetLog::Entry entry(&entry_data, NetLog::LOG_ALL); + NetLog::Entry entry(&entry_data, NetLogCaptureMode::IncludeSocketBytes()); // Add the entry multiple times. logger->OnAddEntry(entry);
diff --git a/net/net.gyp b/net/net.gyp index 5e4ae19..2caae5a 100644 --- a/net/net.gyp +++ b/net/net.gyp
@@ -103,6 +103,7 @@ ], }, { + # GN version: //net 'target_name': 'net', 'dependencies': [ '../base/base.gyp:base_i18n', @@ -120,6 +121,7 @@ 'includes': [ 'net_common.gypi' ], }, { + # GN version: //net:net_unittests 'target_name': 'net_unittests', 'type': '<(gtest_target_type)', 'dependencies': [ @@ -178,6 +180,9 @@ }], [ 'use_nss_certs != 1', { 'sources!': [ + 'cert/nss_cert_database_unittest.cc', + 'cert/nss_cert_database_chromeos_unittest.cc', + 'cert/nss_profile_filter_chromeos_unittest.cc', 'ssl/client_cert_store_chromeos_unittest.cc', 'ssl/client_cert_store_nss_unittest.cc', ], @@ -187,7 +192,8 @@ 'dependencies': [ '../third_party/boringssl/boringssl.gyp:boringssl', ], - }, { # use_openssl == 0 + }], + [ 'use_nss_certs == 1 or OS == "ios" or use_openssl == 0', { 'conditions': [ [ 'desktop_linux == 1 or chromeos == 1', { 'dependencies': [ @@ -199,9 +205,6 @@ '../third_party/nss/nss.gyp:nss', 'third_party/nss/ssl.gyp:libssl', ], - 'sources!': [ - 'cert/nss_cert_database_unittest.cc', - ], }], ], }], @@ -239,9 +242,6 @@ # TODO(bulach): Add equivalent tests when the underlying # functionality is ported to OpenSSL. 'sources!': [ - 'cert/nss_cert_database_chromeos_unittest.cc', - 'cert/nss_cert_database_unittest.cc', - 'cert/nss_profile_filter_chromeos_unittest.cc', 'cert/x509_util_nss_unittest.cc', 'quic/test_tools/crypto_test_utils_nss.cc', ], @@ -741,7 +741,7 @@ # TODO(jschuh): crbug.com/167187 fix size_t to int truncations. 'msvs_disabled_warnings': [4267, ], }, - { + { # GN version: //net:balsa 'target_name': 'balsa', 'type': 'static_library', 'dependencies': [
diff --git a/net/net.gypi b/net/net.gypi index a122009f..e481231 100644 --- a/net/net.gypi +++ b/net/net.gypi
@@ -93,6 +93,14 @@ 'cert/x509_util.h', 'cert/x509_util_openssl.cc', 'cert/x509_util_openssl.h', + 'der/input.cc', + 'der/input.h', + 'der/parse_values.cc', + 'der/parse_values.h', + 'der/parser.cc', + 'der/parser.h', + 'der/tag.cc', + 'der/tag.h', 'http/http_auth_challenge_tokenizer.cc', 'http/http_auth_challenge_tokenizer.h', 'http/http_byte_range.cc', @@ -116,6 +124,8 @@ 'http/transport_security_state.h', 'log/net_log.cc', 'log/net_log.h', + 'log/net_log_capture_mode.cc', + 'log/net_log_capture_mode.h', 'log/net_log_event_type_list.h', 'log/net_log_source_type_list.h', 'socket/client_socket_handle.cc', @@ -140,6 +150,7 @@ 'ssl/openssl_client_key_store.h', 'ssl/openssl_platform_key.h', 'ssl/openssl_platform_key_mac.cc', + 'ssl/openssl_platform_key_nss.cc', 'ssl/openssl_platform_key_win.cc', 'ssl/openssl_ssl_util.cc', 'ssl/openssl_ssl_util.h', @@ -378,6 +389,7 @@ 'cert/x509_util_mac.h', 'cert/x509_util_nss.cc', 'cert/x509_util_nss.h', + 'cert/x509_util_nss_certs.cc', 'cert_net/cert_net_fetcher_impl.cc', 'cert_net/cert_net_fetcher_impl.h', 'cookies/canonical_cookie.cc', @@ -1328,6 +1340,9 @@ 'cookies/cookie_store_unittest.h', 'cookies/cookie_util_unittest.cc', 'cookies/parsed_cookie_unittest.cc', + 'der/input_unittest.cc', + 'der/parser_unittest.cc', + 'der/parse_values_unittest.cc', 'disk_cache/backend_unittest.cc', 'disk_cache/blockfile/addr_unittest.cc', 'disk_cache/blockfile/bitmap_unittest.cc', @@ -1434,8 +1449,9 @@ 'http/transport_security_persister_unittest.cc', 'http/transport_security_state_unittest.cc', 'http/url_security_manager_unittest.cc', - 'log/write_to_file_net_log_observer_unittest.cc', + 'log/net_log_capture_mode_unittest.cc', 'log/net_log_unittest.cc', + 'log/write_to_file_net_log_observer_unittest.cc', 'log/net_log_unittest.h', 'log/net_log_util_unittest.cc', 'log/trace_net_log_observer_unittest.cc', @@ -1604,6 +1620,7 @@ 'socket/deterministic_socket_data_unittest.cc', 'socket/mock_client_socket_pool_manager.cc', 'socket/mock_client_socket_pool_manager.h', + 'socket/sequenced_socket_data_unittest.cc', 'socket/socks5_client_socket_unittest.cc', 'socket/socks_client_socket_pool_unittest.cc', 'socket/socks_client_socket_unittest.cc',
diff --git a/net/net_common.gypi b/net/net_common.gypi index 4bb8235..77134f5 100644 --- a/net/net_common.gypi +++ b/net/net_common.gypi
@@ -125,30 +125,14 @@ }], ['use_openssl==1', { 'sources!': [ - 'base/crypto_module_nss.cc', - 'base/keygen_handler_nss.cc', 'base/nss_memio.c', 'base/nss_memio.h', - 'cert/cert_database_nss.cc', - 'cert/cert_verify_proc_nss.cc', - 'cert/cert_verify_proc_nss.h', 'cert/ct_log_verifier_nss.cc', 'cert/ct_objects_extractor_nss.cc', 'cert/jwk_serializer_nss.cc', - 'cert/nss_cert_database.cc', - 'cert/nss_cert_database.h', - 'cert/nss_cert_database_chromeos.cc', - 'cert/nss_cert_database_chromeos.h', - 'cert/nss_profile_filter_chromeos.cc', - 'cert/nss_profile_filter_chromeos.h', 'cert/scoped_nss_types.h', 'cert/sha256_legacy_support_nss_win.cc', - 'cert/test_root_certs_nss.cc', - 'cert/x509_certificate_nss.cc', 'cert/x509_util_nss.cc', - 'cert/x509_util_nss.h', - 'cert_net/nss_ocsp.cc', - 'cert_net/nss_ocsp.h', 'quic/crypto/aead_base_decrypter_nss.cc', 'quic/crypto/aead_base_encrypter_nss.cc', 'quic/crypto/aes_128_gcm_12_decrypter_nss.cc', @@ -163,12 +147,6 @@ 'socket/ssl_client_socket_nss.h', 'socket/ssl_server_socket_nss.cc', 'socket/ssl_server_socket_nss.h', - 'third_party/mozilla_security_manager/nsKeygenHandler.cpp', - 'third_party/mozilla_security_manager/nsKeygenHandler.h', - 'third_party/mozilla_security_manager/nsNSSCertificateDB.cpp', - 'third_party/mozilla_security_manager/nsNSSCertificateDB.h', - 'third_party/mozilla_security_manager/nsPKCS12Blob.cpp', - 'third_party/mozilla_security_manager/nsPKCS12Blob.h', ], 'dependencies': [ '../third_party/boringssl/boringssl.gyp:boringssl', @@ -176,7 +154,6 @@ }, { # else !use_openssl: remove the unneeded files and depend on NSS. 'sources!': [ - 'base/crypto_module_openssl.cc', 'cert/ct_log_verifier_openssl.cc', 'cert/ct_objects_extractor_openssl.cc', 'cert/jwk_serializer_openssl.cc', @@ -199,12 +176,16 @@ 'socket/ssl_server_socket_openssl.h', 'ssl/openssl_platform_key.h', 'ssl/openssl_platform_key_mac.cc', + 'ssl/openssl_platform_key_nss.cc', 'ssl/openssl_platform_key_win.cc', 'ssl/openssl_ssl_util.cc', 'ssl/openssl_ssl_util.h', 'ssl/ssl_client_session_cache_openssl.cc', 'ssl/ssl_client_session_cache_openssl.h', ], + }, + ], + [ 'use_nss_certs == 1 or OS == "ios" or use_openssl == 0', { 'conditions': [ # Pull in the bundled or system NSS as appropriate. [ 'desktop_linux == 1 or chromeos == 1', { @@ -219,10 +200,15 @@ ], }] ], + }, { + 'sources!': [ + 'cert/x509_util_nss.h', + ], }, ], [ 'use_openssl_certs == 0', { 'sources!': [ + 'base/crypto_module_openssl.cc', 'base/keygen_handler_openssl.cc', 'base/openssl_private_key_store.h', 'base/openssl_private_key_store_android.cc', @@ -264,17 +250,30 @@ }], ], }, - { # else: OS is not in the above list + ], + [ 'use_nss_certs != 1', { 'sources!': [ 'base/crypto_module_nss.cc', 'base/keygen_handler_nss.cc', 'cert/cert_database_nss.cc', + 'cert/cert_verify_proc_nss.cc', + 'cert/cert_verify_proc_nss.h', 'cert/nss_cert_database.cc', 'cert/nss_cert_database.h', + 'cert/nss_cert_database_chromeos.cc', + 'cert/nss_cert_database_chromeos.h', + 'cert/nss_profile_filter_chromeos.cc', + 'cert/nss_profile_filter_chromeos.h', 'cert/test_root_certs_nss.cc', 'cert/x509_certificate_nss.cc', + 'cert/x509_util_nss_certs.cc', 'cert_net/nss_ocsp.cc', 'cert_net/nss_ocsp.h', + 'ssl/client_cert_store_chromeos.cc', + 'ssl/client_cert_store_chromeos.h', + 'ssl/client_cert_store_nss.cc', + 'ssl/client_cert_store_nss.h', + 'ssl/openssl_platform_key_nss.cc', 'third_party/mozilla_security_manager/nsKeygenHandler.cpp', 'third_party/mozilla_security_manager/nsKeygenHandler.h', 'third_party/mozilla_security_manager/nsNSSCertificateDB.cpp', @@ -284,14 +283,12 @@ ], }, ], - [ 'use_nss_certs != 1', { - 'sources!': [ - 'cert/cert_verify_proc_nss.cc', - 'cert/cert_verify_proc_nss.h', - 'ssl/client_cert_store_chromeos.cc', - 'ssl/client_cert_store_chromeos.h', - 'ssl/client_cert_store_nss.cc', - 'ssl/client_cert_store_nss.h', + # client_cert_store_nss.c requires NSS_CmpCertChainWCANames from NSS's + # libssl, but our bundled copy is not built in OpenSSL ports. Pull that + # file in directly. + [ 'use_nss_certs == 1 and use_openssl == 1', { + 'sources': [ + 'third_party/nss/ssl/cmpcert.c', ], }], [ 'enable_websockets != 1', { @@ -416,8 +413,7 @@ ['include', '^cert/cert_verify_proc_nss\\.cc$'], ['include', '^cert/cert_verify_proc_nss\\.h$'], ['include', '^cert/test_root_certs_nss\\.cc$'], - ['include', '^cert/x509_util_nss\\.cc$'], - ['include', '^cert/x509_util_nss\\.h$'], + ['include', '^cert/x509_util_nss_certs\\.cc$'], ['include', '^cert_net/nss_ocsp\\.cc$'], ['include', '^cert_net/nss_ocsp\\.h$'], ['include', '^proxy/proxy_resolver_mac\\.cc$'],
diff --git a/net/net_nacl.gyp b/net/net_nacl.gyp index 2753c21..14c345c 100644 --- a/net/net_nacl.gyp +++ b/net/net_nacl.gyp
@@ -23,7 +23,6 @@ }, 'dependencies': [ '../crypto/crypto_nacl.gyp:crypto_nacl', - '../native_client/tools.gyp:prep_toolchain', '../native_client_sdk/native_client_sdk_untrusted.gyp:nacl_io_untrusted', '../third_party/boringssl/boringssl_nacl.gyp:boringssl_nacl', '../third_party/protobuf/protobuf.gyp:protobuf_lite',
diff --git a/net/proxy/proxy_resolver_v8_tracing.cc b/net/proxy/proxy_resolver_v8_tracing.cc index 230b7caf..3970589 100644 --- a/net/proxy/proxy_resolver_v8_tracing.cc +++ b/net/proxy/proxy_resolver_v8_tracing.cc
@@ -56,7 +56,7 @@ // Returns event parameters for a PAC error message (line number + message). base::Value* NetLogErrorCallback(int line_number, const base::string16* message, - NetLog::LogLevel /* log_level */) { + NetLogCaptureMode /* capture_mode */) { base::DictionaryValue* dict = new base::DictionaryValue(); dict->SetInteger("line_number", line_number); dict->SetString("message", *message);
diff --git a/net/proxy/proxy_script_decider.cc b/net/proxy/proxy_script_decider.cc index 27e7afb..e7d8b17 100644 --- a/net/proxy/proxy_script_decider.cc +++ b/net/proxy/proxy_script_decider.cc
@@ -10,6 +10,7 @@ #include "base/format_macros.h" #include "base/logging.h" #include "base/metrics/histogram.h" +#include "base/profiler/scoped_tracker.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" #include "base/values.h" @@ -55,7 +56,7 @@ base::Value* ProxyScriptDecider::PacSource::NetLogCallback( const GURL* effective_pac_url, - NetLog::LogLevel /* log_level */) const { + NetLogCaptureMode /* capture_mode */) const { base::DictionaryValue* dict = new base::DictionaryValue(); std::string source; switch (type) { @@ -103,6 +104,10 @@ int ProxyScriptDecider::Start( const ProxyConfig& config, const base::TimeDelta wait_delay, bool fetch_pac_bytes, const CompletionCallback& callback) { + // TODO(eroman): Remove ScopedTracker below once crbug.com/455942 is fixed. + tracked_objects::ScopedTracker tracking_profile( + FROM_HERE_WITH_EXPLICIT_FUNCTION("455942 ProxyScriptDecider::Start")); + DCHECK_EQ(STATE_NONE, next_state_); DCHECK(!callback.is_null()); DCHECK(config.HasAutomaticSettings()); @@ -221,6 +226,10 @@ } int ProxyScriptDecider::DoWait() { + // TODO(eroman): Remove ScopedTracker below once crbug.com/455942 is fixed. + tracked_objects::ScopedTracker tracking_profile( + FROM_HERE_WITH_EXPLICIT_FUNCTION("455942 ProxyScriptDecider::DoWait")); + next_state_ = STATE_WAIT_COMPLETE; // If no waiting is required, continue on to the next state. @@ -235,6 +244,11 @@ } int ProxyScriptDecider::DoWaitComplete(int result) { + // TODO(eroman): Remove ScopedTracker below once crbug.com/455942 is fixed. + tracked_objects::ScopedTracker tracking_profile( + FROM_HERE_WITH_EXPLICIT_FUNCTION( + "455942 ProxyScriptDecider::DoWaitComplete")); + DCHECK_EQ(OK, result); if (wait_delay_.ToInternalValue() != 0) { net_log_.EndEventWithNetErrorCode(NetLog::TYPE_PROXY_SCRIPT_DECIDER_WAIT, @@ -248,6 +262,11 @@ } int ProxyScriptDecider::DoQuickCheck() { + // TODO(eroman): Remove ScopedTracker below once crbug.com/455942 is fixed. + tracked_objects::ScopedTracker tracking_profile( + FROM_HERE_WITH_EXPLICIT_FUNCTION( + "455942 ProxyScriptDecider::DoQuickCheck")); + DCHECK(quick_check_enabled_); if (host_resolver_.get() == NULL) { // If we have no resolver, skip QuickCheck altogether. @@ -275,6 +294,11 @@ } int ProxyScriptDecider::DoQuickCheckComplete(int result) { + // TODO(eroman): Remove ScopedTracker below once crbug.com/455942 is fixed. + tracked_objects::ScopedTracker tracking_profile( + FROM_HERE_WITH_EXPLICIT_FUNCTION( + "455942 ProxyScriptDecider::DoQuickCheckComplete")); + DCHECK(quick_check_enabled_); base::TimeDelta delta = base::Time::Now() - quick_check_start_time_; if (result == OK) @@ -290,6 +314,11 @@ } int ProxyScriptDecider::DoFetchPacScript() { + // TODO(eroman): Remove ScopedTracker below once crbug.com/455942 is fixed. + tracked_objects::ScopedTracker tracking_profile( + FROM_HERE_WITH_EXPLICIT_FUNCTION( + "455942 ProxyScriptDecider::DoFetchPacScript")); + DCHECK(fetch_pac_bytes_); next_state_ = STATE_FETCH_PAC_SCRIPT_COMPLETE; @@ -338,6 +367,11 @@ } int ProxyScriptDecider::DoVerifyPacScript() { + // TODO(eroman): Remove ScopedTracker below once crbug.com/455942 is fixed. + tracked_objects::ScopedTracker tracking_profile( + FROM_HERE_WITH_EXPLICIT_FUNCTION( + "455942 ProxyScriptDecider::DoVerifyPacScript")); + next_state_ = STATE_VERIFY_PAC_SCRIPT_COMPLETE; // This is just a heuristic. Ideally we would try to parse the script. @@ -348,6 +382,11 @@ } int ProxyScriptDecider::DoVerifyPacScriptComplete(int result) { + // TODO(eroman): Remove ScopedTracker below once crbug.com/455942 is fixed. + tracked_objects::ScopedTracker tracking_profile( + FROM_HERE_WITH_EXPLICIT_FUNCTION( + "455942 ProxyScriptDecider::DoVerifyPacScriptComplete")); + if (result != OK) return TryToFallbackPacSource(result);
diff --git a/net/proxy/proxy_script_decider.h b/net/proxy/proxy_script_decider.h index 271e43ae..aa37c88 100644 --- a/net/proxy/proxy_script_decider.h +++ b/net/proxy/proxy_script_decider.h
@@ -102,7 +102,7 @@ // be non-NULL and point to the URL derived from information contained in // |this|, if Type is not WPAD_DHCP. base::Value* NetLogCallback(const GURL* effective_pac_url, - NetLog::LogLevel log_level) const; + NetLogCaptureMode capture_mode) const; Type type; GURL url; // Empty unless |type == PAC_SOURCE_CUSTOM|.
diff --git a/net/proxy/proxy_service.cc b/net/proxy/proxy_service.cc index f912d2e..7de5449 100644 --- a/net/proxy/proxy_service.cc +++ b/net/proxy/proxy_service.cc
@@ -316,7 +316,7 @@ base::Value* NetLogProxyConfigChangedCallback( const ProxyConfig* old_config, const ProxyConfig* new_config, - NetLog::LogLevel /* log_level */) { + NetLogCaptureMode /* capture_mode */) { base::DictionaryValue* dict = new base::DictionaryValue(); // The "old_config" is optional -- the first notification will not have // any "previous" configuration. @@ -327,7 +327,7 @@ } base::Value* NetLogBadProxyListCallback(const ProxyRetryInfoMap* retry_info, - NetLog::LogLevel /* log_level */) { + NetLogCaptureMode /* capture_mode */) { base::DictionaryValue* dict = new base::DictionaryValue(); base::ListValue* list = new base::ListValue(); @@ -342,7 +342,7 @@ // Returns NetLog parameters on a successfuly proxy resolution. base::Value* NetLogFinishedResolvingProxyCallback( const ProxyInfo* result, - NetLog::LogLevel /* log_level */) { + NetLogCaptureMode /* capture_mode */) { base::DictionaryValue* dict = new base::DictionaryValue(); dict->SetString("pac_string", result->ToPacString()); return dict; @@ -1389,7 +1389,7 @@ network_delegate->NotifyResolveProxy(url, load_flags, *this, result); // When logging all events is enabled, dump the proxy list. - if (net_log.IsLogging()) { + if (net_log.GetCaptureMode().enabled()) { net_log.AddEvent( NetLog::TYPE_PROXY_SERVICE_RESOLVED_PROXY_LIST, base::Bind(&NetLogFinishedResolvingProxyCallback, result));
diff --git a/net/quic/quic_client_session.cc b/net/quic/quic_client_session.cc index 0dfee92..b5f1d98 100644 --- a/net/quic/quic_client_session.cc +++ b/net/quic/quic_client_session.cc
@@ -99,7 +99,7 @@ base::Value* NetLogQuicClientSessionCallback( const QuicServerId* server_id, bool require_confirmation, - NetLog::LogLevel /* log_level */) { + NetLogCaptureMode /* capture_mode */) { base::DictionaryValue* dict = new base::DictionaryValue(); dict->SetString("host", server_id->host()); dict->SetInteger("port", server_id->port());
diff --git a/net/quic/quic_connection_logger.cc b/net/quic/quic_connection_logger.cc index 408eb57b..fd60d8e8 100644 --- a/net/quic/quic_connection_logger.cc +++ b/net/quic/quic_connection_logger.cc
@@ -38,7 +38,7 @@ base::Value* NetLogQuicPacketCallback(const IPEndPoint* self_address, const IPEndPoint* peer_address, size_t packet_size, - NetLog::LogLevel /* log_level */) { + NetLogCaptureMode /* capture_mode */) { base::DictionaryValue* dict = new base::DictionaryValue(); dict->SetString("self_address", self_address->ToString()); dict->SetString("peer_address", peer_address->ToString()); @@ -52,7 +52,7 @@ TransmissionType transmission_type, size_t packet_size, QuicTime sent_time, - NetLog::LogLevel /* log_level */) { + NetLogCaptureMode /* capture_mode */) { base::DictionaryValue* dict = new base::DictionaryValue(); dict->SetInteger("encryption_level", level); dict->SetInteger("transmission_type", transmission_type); @@ -67,7 +67,7 @@ base::Value* NetLogQuicPacketRetransmittedCallback( QuicPacketSequenceNumber old_sequence_number, QuicPacketSequenceNumber new_sequence_number, - NetLog::LogLevel /* log_level */) { + NetLogCaptureMode /* capture_mode */) { base::DictionaryValue* dict = new base::DictionaryValue(); dict->SetString("old_packet_sequence_number", base::Uint64ToString(old_sequence_number)); @@ -76,8 +76,9 @@ return dict; } -base::Value* NetLogQuicPacketHeaderCallback(const QuicPacketHeader* header, - NetLog::LogLevel /* log_level */) { +base::Value* NetLogQuicPacketHeaderCallback( + const QuicPacketHeader* header, + NetLogCaptureMode /* capture_mode */) { base::DictionaryValue* dict = new base::DictionaryValue(); dict->SetString("connection_id", base::Uint64ToString(header->public_header.connection_id)); @@ -91,8 +92,9 @@ return dict; } -base::Value* NetLogQuicStreamFrameCallback(const QuicStreamFrame* frame, - NetLog::LogLevel /* log_level */) { +base::Value* NetLogQuicStreamFrameCallback( + const QuicStreamFrame* frame, + NetLogCaptureMode /* capture_mode */) { base::DictionaryValue* dict = new base::DictionaryValue(); dict->SetInteger("stream_id", frame->stream_id); dict->SetBoolean("fin", frame->fin); @@ -102,7 +104,7 @@ } base::Value* NetLogQuicAckFrameCallback(const QuicAckFrame* frame, - NetLog::LogLevel /* log_level */) { + NetLogCaptureMode /* capture_mode */) { base::DictionaryValue* dict = new base::DictionaryValue(); dict->SetString("largest_observed", base::Uint64ToString(frame->largest_observed)); @@ -146,7 +148,7 @@ base::Value* NetLogQuicRstStreamFrameCallback( const QuicRstStreamFrame* frame, - NetLog::LogLevel /* log_level */) { + NetLogCaptureMode /* capture_mode */) { base::DictionaryValue* dict = new base::DictionaryValue(); dict->SetInteger("stream_id", frame->stream_id); dict->SetInteger("quic_rst_stream_error", frame->error_code); @@ -156,7 +158,7 @@ base::Value* NetLogQuicConnectionCloseFrameCallback( const QuicConnectionCloseFrame* frame, - NetLog::LogLevel /* log_level */) { + NetLogCaptureMode /* capture_mode */) { base::DictionaryValue* dict = new base::DictionaryValue(); dict->SetInteger("quic_error", frame->error_code); dict->SetString("details", frame->error_details); @@ -165,7 +167,7 @@ base::Value* NetLogQuicWindowUpdateFrameCallback( const QuicWindowUpdateFrame* frame, - NetLog::LogLevel /* log_level */) { + NetLogCaptureMode /* capture_mode */) { base::DictionaryValue* dict = new base::DictionaryValue(); dict->SetInteger("stream_id", frame->stream_id); dict->SetString("byte_offset", base::Uint64ToString(frame->byte_offset)); @@ -174,7 +176,7 @@ base::Value* NetLogQuicBlockedFrameCallback( const QuicBlockedFrame* frame, - NetLog::LogLevel /* log_level */) { + NetLogCaptureMode /* capture_mode */) { base::DictionaryValue* dict = new base::DictionaryValue(); dict->SetInteger("stream_id", frame->stream_id); return dict; @@ -182,7 +184,7 @@ base::Value* NetLogQuicGoAwayFrameCallback( const QuicGoAwayFrame* frame, - NetLog::LogLevel /* log_level */) { + NetLogCaptureMode /* capture_mode */) { base::DictionaryValue* dict = new base::DictionaryValue(); dict->SetInteger("quic_error", frame->error_code); dict->SetInteger("last_good_stream_id", frame->last_good_stream_id); @@ -192,7 +194,7 @@ base::Value* NetLogQuicStopWaitingFrameCallback( const QuicStopWaitingFrame* frame, - NetLog::LogLevel /* log_level */) { + NetLogCaptureMode /* capture_mode */) { base::DictionaryValue* dict = new base::DictionaryValue(); base::DictionaryValue* sent_info = new base::DictionaryValue(); dict->Set("sent_info", sent_info); @@ -203,7 +205,7 @@ base::Value* NetLogQuicVersionNegotiationPacketCallback( const QuicVersionNegotiationPacket* packet, - NetLog::LogLevel /* log_level */) { + NetLogCaptureMode /* capture_mode */) { base::DictionaryValue* dict = new base::DictionaryValue(); base::ListValue* versions = new base::ListValue(); dict->Set("versions", versions); @@ -216,7 +218,7 @@ base::Value* NetLogQuicCryptoHandshakeMessageCallback( const CryptoHandshakeMessage* message, - NetLog::LogLevel /* log_level */) { + NetLogCaptureMode /* capture_mode */) { base::DictionaryValue* dict = new base::DictionaryValue(); dict->SetString("quic_crypto_handshake_message", message->DebugString()); return dict; @@ -225,7 +227,7 @@ base::Value* NetLogQuicOnConnectionClosedCallback( QuicErrorCode error, bool from_peer, - NetLog::LogLevel /* log_level */) { + NetLogCaptureMode /* capture_mode */) { base::DictionaryValue* dict = new base::DictionaryValue(); dict->SetInteger("quic_error", error); dict->SetBoolean("from_peer", from_peer); @@ -234,7 +236,7 @@ base::Value* NetLogQuicCertificateVerifiedCallback( scoped_refptr<X509Certificate> cert, - NetLog::LogLevel /* log_level */) { + NetLogCaptureMode /* capture_mode */) { // Only the subjects are logged so that we can investigate connection pooling. // More fields could be logged in the future. std::vector<std::string> dns_names;
diff --git a/net/quic/quic_http_utils.cc b/net/quic/quic_http_utils.cc index 2bad8a6a..9a1c6bb 100644 --- a/net/quic/quic_http_utils.cc +++ b/net/quic/quic_http_utils.cc
@@ -20,13 +20,12 @@ IDLE : static_cast<RequestPriority>(HIGHEST - priority); } -base::Value* QuicRequestNetLogCallback( - QuicStreamId stream_id, - const SpdyHeaderBlock* headers, - QuicPriority priority, - NetLog::LogLevel log_level) { +base::Value* QuicRequestNetLogCallback(QuicStreamId stream_id, + const SpdyHeaderBlock* headers, + QuicPriority priority, + NetLogCaptureMode capture_mode) { base::DictionaryValue* dict = static_cast<base::DictionaryValue*>( - SpdyHeaderBlockNetLogCallback(headers, log_level)); + SpdyHeaderBlockNetLogCallback(headers, capture_mode)); dict->SetInteger("quic_priority", static_cast<int>(priority)); dict->SetInteger("quic_stream_id", static_cast<int>(stream_id)); return dict;
diff --git a/net/quic/quic_http_utils.h b/net/quic/quic_http_utils.h index 862b7c61..6eafbc0 100644 --- a/net/quic/quic_http_utils.h +++ b/net/quic/quic_http_utils.h
@@ -25,7 +25,7 @@ QuicStreamId stream_id, const SpdyHeaderBlock* headers, QuicPriority priority, - NetLog::LogLevel log_level); + NetLogCaptureMode capture_mode); } // namespace net
diff --git a/net/quic/quic_network_transaction_unittest.cc b/net/quic/quic_network_transaction_unittest.cc index 0fcb77b..67428f72d 100644 --- a/net/quic/quic_network_transaction_unittest.cc +++ b/net/quic/quic_network_transaction_unittest.cc
@@ -67,18 +67,26 @@ // Simplify ownership issues and the interaction with the MockSocketFactory. class MockQuicData { public: + MockQuicData() : sequence_number_(0) {} + ~MockQuicData() { STLDeleteElements(&packets_); } - void AddRead(scoped_ptr<QuicEncryptedPacket> packet) { + void AddSynchronousRead(scoped_ptr<QuicEncryptedPacket> packet) { reads_.push_back(MockRead(SYNCHRONOUS, packet->data(), packet->length(), sequence_number_++)); packets_.push_back(packet.release()); } + void AddRead(scoped_ptr<QuicEncryptedPacket> packet) { + reads_.push_back( + MockRead(ASYNC, packet->data(), packet->length(), sequence_number_++)); + packets_.push_back(packet.release()); + } + void AddRead(IoMode mode, int rv) { - reads_.push_back(MockRead(mode, rv)); + reads_.push_back(MockRead(mode, rv, sequence_number_++)); } void AddWrite(scoped_ptr<QuicEncryptedPacket> packet) { @@ -87,12 +95,11 @@ packets_.push_back(packet.release()); } - void AddDelayedSocketDataToFactory(MockClientSocketFactory* factory, - size_t delay) { + void AddSocketDataToFactory(MockClientSocketFactory* factory) { MockRead* reads = reads_.empty() ? nullptr : &reads_[0]; MockWrite* writes = writes_.empty() ? nullptr : &writes_[0]; - socket_data_.reset(new DelayedSocketData( - delay, reads, reads_.size(), writes, writes_.size())); + socket_data_.reset( + new SequencedSocketData(reads, reads_.size(), writes, writes_.size())); factory->AddSocketDataProvider(socket_data_.get()); } @@ -394,7 +401,7 @@ mock_quic_data.AddWrite(ConstructAckPacket(2, 1)); mock_quic_data.AddRead(SYNCHRONOUS, 0); // EOF - mock_quic_data.AddDelayedSocketDataToFactory(&socket_factory_, 1); + mock_quic_data.AddSocketDataToFactory(&socket_factory_); // The non-alternate protocol job needs to hang in order to guarantee that // the alternate-protocol job will "win". @@ -457,7 +464,7 @@ mock_quic_data.AddWrite(ConstructAckPacket(2, 1)); mock_quic_data.AddRead(SYNCHRONOUS, 0); // EOF - mock_quic_data.AddDelayedSocketDataToFactory(&socket_factory_, 1); + mock_quic_data.AddSocketDataToFactory(&socket_factory_); // There is no need to set up an alternate protocol job, because // no attempt will be made to speak to the proxy over TCP. @@ -474,7 +481,7 @@ MockQuicData mock_quic_data; mock_quic_data.AddRead(ASYNC, ERR_SOCKET_NOT_CONNECTED); - mock_quic_data.AddDelayedSocketDataToFactory(&socket_factory_, 0); + mock_quic_data.AddSocketDataToFactory(&socket_factory_); CreateSession(); @@ -533,7 +540,7 @@ mock_quic_data.AddWrite(ConstructAckPacket(2, 1)); mock_quic_data.AddRead(SYNCHRONOUS, 0); // EOF - mock_quic_data.AddDelayedSocketDataToFactory(&socket_factory_, 1); + mock_quic_data.AddSocketDataToFactory(&socket_factory_); // The non-alternate protocol job needs to hang in order to guarantee that // the alternate-protocol job will "win". @@ -568,7 +575,7 @@ mock_quic_data.AddWrite(ConstructAckPacket(2, 1)); mock_quic_data.AddRead(SYNCHRONOUS, 0); // EOF - mock_quic_data.AddDelayedSocketDataToFactory(&socket_factory_, 1); + mock_quic_data.AddSocketDataToFactory(&socket_factory_); // The non-alternate protocol job needs to hang in order to guarantee that // the alternate-protocol job will "win". @@ -603,7 +610,7 @@ mock_quic_data.AddWrite(ConstructAckPacket(2, 1)); mock_quic_data.AddRead(SYNCHRONOUS, 0); // EOF - mock_quic_data.AddDelayedSocketDataToFactory(&socket_factory_, 1); + mock_quic_data.AddSocketDataToFactory(&socket_factory_); // The non-alternate protocol job needs to hang in order to guarantee that // the alternate-protocol job will "win". @@ -652,7 +659,7 @@ mock_quic_data.AddWrite(ConstructAckPacket(2, 1)); mock_quic_data.AddRead(SYNCHRONOUS, 0); // EOF - mock_quic_data.AddDelayedSocketDataToFactory(&socket_factory_, 1); + mock_quic_data.AddSocketDataToFactory(&socket_factory_); // The non-alternate protocol job needs to hang in order to guarantee that // the alternate-protocol job will "win". @@ -736,7 +743,7 @@ mock_quic_data.AddWrite(ConstructAckPacket(2, 1)); mock_quic_data.AddRead(SYNCHRONOUS, 0); // EOF - mock_quic_data.AddDelayedSocketDataToFactory(&socket_factory_, 1); + mock_quic_data.AddSocketDataToFactory(&socket_factory_); // The non-alternate protocol job needs to hang in order to guarantee that // the alternate-protocol job will "win". @@ -819,7 +826,7 @@ mock_quic_data.AddWrite(ConstructAckPacket(2, 1)); mock_quic_data.AddRead(SYNCHRONOUS, 0); // EOF - mock_quic_data.AddDelayedSocketDataToFactory(&socket_factory_, 1); + mock_quic_data.AddSocketDataToFactory(&socket_factory_); // The non-alternate protocol job needs to hang in order to guarantee that // the alternate-protocol job will "win". @@ -842,7 +849,7 @@ ConstructDataPacket(2, kClientDataStreamId1, false, true, 0, "hello!")); mock_quic_data.AddWrite(ConstructAckPacket(2, 1)); mock_quic_data.AddRead(SYNCHRONOUS, 0); // EOF - mock_quic_data.AddDelayedSocketDataToFactory(&socket_factory_, 1); + mock_quic_data.AddSocketDataToFactory(&socket_factory_); // In order for a new QUIC session to be established via alternate-protocol // without racing an HTTP connection, we need the host resolution to happen @@ -916,7 +923,7 @@ ConstructDataPacket(2, kClientDataStreamId1, false, true, 0, "hello!")); mock_quic_data.AddWrite(ConstructAckPacket(2, 1)); mock_quic_data.AddRead(SYNCHRONOUS, 0); // EOF - mock_quic_data.AddDelayedSocketDataToFactory(&socket_factory_, 1); + mock_quic_data.AddSocketDataToFactory(&socket_factory_); // The non-alternate protocol job needs to hang in order to guarantee that // the alternate-protocol job will "win". @@ -1133,12 +1140,12 @@ TEST_P(QuicNetworkTransactionTest, ConnectionCloseDuringConnect) { MockQuicData mock_quic_data; - mock_quic_data.AddRead(ConstructConnectionClosePacket(1)); + mock_quic_data.AddSynchronousRead(ConstructConnectionClosePacket(1)); mock_quic_data.AddWrite( ConstructRequestHeadersPacket(1, kClientDataStreamId1, true, true, GetRequestHeaders("GET", "http", "/"))); mock_quic_data.AddWrite(ConstructAckPacket(2, 1)); - mock_quic_data.AddDelayedSocketDataToFactory(&socket_factory_, 0); + mock_quic_data.AddSocketDataToFactory(&socket_factory_); // When the QUIC connection fails, we will try the request again over HTTP. MockRead http_reads[] = {
diff --git a/net/socket/client_socket_pool_base.cc b/net/socket/client_socket_pool_base.cc index f6e9671..3d0ff567 100644 --- a/net/socket/client_socket_pool_base.cc +++ b/net/socket/client_socket_pool_base.cc
@@ -4,6 +4,8 @@ #include "net/socket/client_socket_pool_base.h" +#include <algorithm> + #include "base/compiler_specific.h" #include "base/format_macros.h" #include "base/logging.h" @@ -573,13 +575,8 @@ const Group& group = *group_it->second; if (group.HasConnectJobForHandle(handle)) { - // Just return the state of the farthest along ConnectJob for the first - // group.jobs().size() pending requests. - LoadState max_state = LOAD_STATE_IDLE; - for (const auto& job : group.jobs()) { - max_state = std::max(max_state, job->GetLoadState()); - } - return max_state; + // Just return the state of the oldest ConnectJob. + return (*group.jobs().begin())->GetLoadState(); } if (group.CanUseAdditionalSocketSlot(max_sockets_per_group_)) @@ -629,7 +626,7 @@ group_dict->Set("idle_sockets", idle_socket_list); base::ListValue* connect_jobs_list = new base::ListValue(); - std::set<ConnectJob*>::const_iterator job = group->jobs().begin(); + std::list<ConnectJob*>::const_iterator job = group->jobs().begin(); for (job = group->jobs().begin(); job != group->jobs().end(); job++) { int source_id = (*job)->net_log().source().id; connect_jobs_list->Append(new base::FundamentalValue(source_id)); @@ -1197,19 +1194,16 @@ if (is_preconnect) ++unassigned_job_count_; - jobs_.insert(job.release()); + jobs_.push_back(job.release()); } void ClientSocketPoolBaseHelper::Group::RemoveJob(ConnectJob* job) { scoped_ptr<ConnectJob> owned_job(job); SanityCheck(); - std::set<ConnectJob*>::iterator it = jobs_.find(job); - if (it != jobs_.end()) { - jobs_.erase(it); - } else { - NOTREACHED(); - } + // Check that |job| is in the list. + DCHECK_EQ(*std::find(jobs_.begin(), jobs_.end(), job), job); + jobs_.remove(job); size_t job_count = jobs_.size(); if (job_count < unassigned_job_count_) unassigned_job_count_ = job_count;
diff --git a/net/socket/client_socket_pool_base.h b/net/socket/client_socket_pool_base.h index 4892780..6997e1d 100644 --- a/net/socket/client_socket_pool_base.h +++ b/net/socket/client_socket_pool_base.h
@@ -450,7 +450,7 @@ void DecrementActiveSocketCount() { active_socket_count_--; } int unassigned_job_count() const { return unassigned_job_count_; } - const std::set<ConnectJob*>& jobs() const { return jobs_; } + const std::list<ConnectJob*>& jobs() const { return jobs_; } const std::list<IdleSocket>& idle_sockets() const { return idle_sockets_; } int active_socket_count() const { return active_socket_count_; } std::list<IdleSocket>* mutable_idle_sockets() { return &idle_sockets_; } @@ -479,7 +479,7 @@ size_t unassigned_job_count_; std::list<IdleSocket> idle_sockets_; - std::set<ConnectJob*> jobs_; + std::list<ConnectJob*> jobs_; RequestQueue pending_requests_; int active_socket_count_; // number of active sockets used by clients // A timer for when to start the backup job. @@ -846,9 +846,9 @@ explicit ConnectJobFactoryAdaptor(ConnectJobFactory* connect_job_factory) : connect_job_factory_(connect_job_factory) {} - virtual ~ConnectJobFactoryAdaptor() {} + ~ConnectJobFactoryAdaptor() override {} - virtual scoped_ptr<ConnectJob> NewConnectJob( + scoped_ptr<ConnectJob> NewConnectJob( const std::string& group_name, const internal::ClientSocketPoolBaseHelper::Request& request, ConnectJob::Delegate* delegate) const override {
diff --git a/net/socket/client_socket_pool_base_unittest.cc b/net/socket/client_socket_pool_base_unittest.cc index e54259d..2de67bd 100644 --- a/net/socket/client_socket_pool_base_unittest.cc +++ b/net/socket/client_socket_pool_base_unittest.cc
@@ -1955,48 +1955,63 @@ } // Test GetLoadState in the case there are two socket requests. +// Only the first connection in the pool should affect the pool's load status. TEST_F(ClientSocketPoolBaseTest, LoadStateTwoRequests) { CreatePool(2, 2); connect_job_factory_->set_job_type(TestConnectJob::kMockWaitingJob); ClientSocketHandle handle; TestCompletionCallback callback; - int rv = handle.Init("a", - params_, - DEFAULT_PRIORITY, - callback.callback(), - pool_.get(), - BoundNetLog()); + int rv = handle.Init("a", params_, DEFAULT_PRIORITY, callback.callback(), + pool_.get(), BoundNetLog()); + EXPECT_EQ(ERR_IO_PENDING, rv); + client_socket_factory_.SetJobLoadState(0, LOAD_STATE_RESOLVING_HOST); + + ClientSocketHandle handle2; + TestCompletionCallback callback2; + rv = handle2.Init("a", params_, DEFAULT_PRIORITY, callback2.callback(), + pool_.get(), BoundNetLog()); + EXPECT_EQ(ERR_IO_PENDING, rv); + client_socket_factory_.SetJobLoadState(1, LOAD_STATE_RESOLVING_HOST); + + // Check that both handles report the state of the first job. + EXPECT_EQ(LOAD_STATE_RESOLVING_HOST, handle.GetLoadState()); + EXPECT_EQ(LOAD_STATE_RESOLVING_HOST, handle2.GetLoadState()); + + client_socket_factory_.SetJobLoadState(0, LOAD_STATE_CONNECTING); + + // Check that both handles change to LOAD_STATE_CONNECTING. + EXPECT_EQ(LOAD_STATE_CONNECTING, handle.GetLoadState()); + EXPECT_EQ(LOAD_STATE_CONNECTING, handle2.GetLoadState()); +} + +// Test that the second connection request does not affect the pool's load +// status. +TEST_F(ClientSocketPoolBaseTest, LoadStateTwoRequestsChangeSecondRequestState) { + CreatePool(2, 2); + connect_job_factory_->set_job_type(TestConnectJob::kMockWaitingJob); + + ClientSocketHandle handle; + TestCompletionCallback callback; + int rv = handle.Init("a", params_, DEFAULT_PRIORITY, callback.callback(), + pool_.get(), BoundNetLog()); EXPECT_EQ(ERR_IO_PENDING, rv); ClientSocketHandle handle2; TestCompletionCallback callback2; - rv = handle2.Init("a", - params_, - DEFAULT_PRIORITY, - callback2.callback(), - pool_.get(), - BoundNetLog()); + rv = handle2.Init("a", params_, DEFAULT_PRIORITY, callback2.callback(), + pool_.get(), BoundNetLog()); EXPECT_EQ(ERR_IO_PENDING, rv); + client_socket_factory_.SetJobLoadState(1, LOAD_STATE_RESOLVING_HOST); - // If the first Job is in an earlier state than the second, the state of - // the second job should be used for both handles. - client_socket_factory_.SetJobLoadState(0, LOAD_STATE_RESOLVING_HOST); EXPECT_EQ(LOAD_STATE_CONNECTING, handle.GetLoadState()); EXPECT_EQ(LOAD_STATE_CONNECTING, handle2.GetLoadState()); - // If the second Job is in an earlier state than the second, the state of - // the first job should be used for both handles. - client_socket_factory_.SetJobLoadState(0, LOAD_STATE_SSL_HANDSHAKE); - // One request is farther - EXPECT_EQ(LOAD_STATE_SSL_HANDSHAKE, handle.GetLoadState()); - EXPECT_EQ(LOAD_STATE_SSL_HANDSHAKE, handle2.GetLoadState()); - - // Farthest along job connects and the first request gets the socket. The + // First job connects and the first request gets the socket. The // second handle switches to the state of the remaining ConnectJob. client_socket_factory_.SignalJob(0); EXPECT_EQ(OK, callback.WaitForResult()); - EXPECT_EQ(LOAD_STATE_CONNECTING, handle2.GetLoadState()); + EXPECT_EQ(LOAD_STATE_RESOLVING_HOST, handle2.GetLoadState()); } // Test GetLoadState in the case the per-group limit is reached.
diff --git a/net/socket/nss_ssl_util.cc b/net/socket/nss_ssl_util.cc index e0a22571..e98193b5 100644 --- a/net/socket/nss_ssl_util.cc +++ b/net/socket/nss_ssl_util.cc
@@ -81,7 +81,7 @@ base::Value* NetLogSSLErrorCallback(int net_error, int ssl_lib_error, - NetLog::LogLevel /* log_level */) { + NetLogCaptureMode /* capture_mode */) { base::DictionaryValue* dict = new base::DictionaryValue(); dict->SetInteger("net_error", net_error); if (ssl_lib_error) @@ -385,7 +385,7 @@ const char* function, const char* param, int ssl_lib_error, - NetLog::LogLevel /* log_level */) { + NetLogCaptureMode /* capture_mode */) { base::DictionaryValue* dict = new base::DictionaryValue(); dict->SetString("function", function); if (param[0] != '\0')
diff --git a/net/socket/sequenced_socket_data_unittest.cc b/net/socket/sequenced_socket_data_unittest.cc new file mode 100644 index 0000000..e234b993 --- /dev/null +++ b/net/socket/sequenced_socket_data_unittest.cc
@@ -0,0 +1,1057 @@ +// 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 <string> + +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "base/run_loop.h" +#include "net/base/io_buffer.h" +#include "net/base/test_completion_callback.h" +#include "net/socket/client_socket_handle.h" +#include "net/socket/socket_test_util.h" +#include "net/socket/transport_client_socket_pool.h" +#include "testing/gtest/include/gtest/gtest-spi.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "testing/platform_test.h" + +//----------------------------------------------------------------------------- + +namespace net { + +namespace { + +const char kMsg1[] = "\0hello!\xff"; +const int kLen1 = arraysize(kMsg1); +const char kMsg2[] = "\0a2345678\0"; +const int kLen2 = arraysize(kMsg2); +const char kMsg3[] = "bye!"; +const int kLen3 = arraysize(kMsg3); +const char kMsg4[] = "supercalifragilisticexpialidocious"; +const int kLen4 = arraysize(kMsg4); + +// Helper class for starting the next operation operation reentrantly after the +// previous operation completed asynchronously. When OnIOComplete is called, +// it will first verify that the previous operation behaved as expected. This is +// specified by either SetExpectedRead or SetExpectedWrite. It will then invoke +// a read or write operation specified by SetInvokeRead or SetInvokeWrite. +class ReentrantHelper { + public: + ReentrantHelper(StreamSocket* socket) + : socket_(socket), + verify_read_(false), + first_read_data_(nullptr), + first_len_(-1), + second_read_(false), + second_write_data_(nullptr), + second_len_(-1) {} + + // Expect that the previous operation will return |first_len| and will fill + // |first_read_data_| with |first_read_data|. + void SetExpectedRead(const char* first_read_data, int first_len) { + verify_read_ = true; + first_read_buf_ = new IOBuffer(first_len); + first_read_data_ = first_read_data; + first_len_ = first_len; + } + + // Expect that the previous operation will return |first_len|. + void SetExpectedWrite(int first_len) { + verify_read_ = false; + first_len_ = first_len; + } + + // After verifying expectations, invoke a read of |read_len| bytes into + // |read_buf|, notifying |callback| when complete. + void SetInvokeRead(scoped_refptr<IOBuffer> read_buf, + int read_len, + int second_rv, + CompletionCallback callback) { + second_read_ = true; + second_read_buf_ = read_buf; + second_rv_ = second_rv; + second_callback_ = callback; + second_len_ = read_len; + } + + // After verifying expectations, invoke a write of |write_len| bytes from + // |write_data|, notifying |callback| when complete. + void SetInvokeWrite(const char* write_data, + int write_len, + int second_rv, + CompletionCallback callback) { + second_read_ = false; + second_rv_ = second_rv; + second_write_data_ = write_data; + second_callback_ = callback; + second_len_ = write_len; + } + + // Returns the OnIOComplete callback for this helper. + CompletionCallback callback() { + return base::Bind(&ReentrantHelper::OnIOComplete, base::Unretained(this)); + } + + // Retuns the buffer where data is expected to have been written, + // when checked by SetExpectRead() + scoped_refptr<IOBuffer> read_buf() { return first_read_buf_; } + + private: + void OnIOComplete(int rv) { + CHECK_NE(-1, first_len_) << "Expectation not set."; + CHECK_NE(-1, second_len_) << "Invocation not set."; + ASSERT_EQ(first_len_, rv); + if (verify_read_) { + ASSERT_EQ(std::string(first_read_data_, first_len_), + std::string(first_read_buf_->data(), rv)); + } + + if (second_read_) { + ASSERT_EQ(second_rv_, socket_->Read(second_read_buf_.get(), second_len_, + second_callback_)); + } else { + scoped_refptr<IOBuffer> write_buf = new IOBuffer(second_len_); + memcpy(write_buf->data(), second_write_data_, second_len_); + ASSERT_EQ(second_rv_, + socket_->Write(write_buf.get(), second_len_, second_callback_)); + } + } + + StreamSocket* socket_; + + bool verify_read_; + scoped_refptr<IOBuffer> first_read_buf_; + const char* first_read_data_; + int first_len_; + + CompletionCallback second_callback_; + bool second_read_; + int second_rv_; + scoped_refptr<IOBuffer> second_read_buf_; + const char* second_write_data_; + int second_len_; + + DISALLOW_COPY_AND_ASSIGN(ReentrantHelper); +}; + +class SequencedSocketDataTest : public testing::Test { + public: + SequencedSocketDataTest(); + ~SequencedSocketDataTest() override; + + // This method is used as the completion callback for an async read + // operation and when invoked, it verifies that the correct data was read, + // then reads from the socket and verifies that that it returns the correct + // value. + void ReentrantReadCallback(const char* data, + int len1, + int len2, + int expected_rv2, + int rv); + + // This method is used at the completion callback for an async operation. + // When executed, verifies that |rv| equals |expected_rv| and then + // attempts an aync read from the socket into |read_buf_| (initialized + // to |read_buf_len|) using |callback|. + void ReentrantAsyncReadCallback(int len1, int len2, int rv); + + // This method is used as the completion callback for an async write + // operation and when invoked, it verifies that the write returned correctly, + // then + // attempts to write to the socket and verifies that that it returns the + // correct value. + void ReentrantWriteCallback(int expected_rv1, + const char* data, + int len, + int expected_rv2, + int rv); + + // This method is used at the completion callback for an async operation. + // When executed, verifies that |rv| equals |expected_rv| and then + // attempts an aync write of |data| with |callback| + void ReentrantAsyncWriteCallback(const char* data, + int len, + CompletionCallback callback, + int expected_rv, + int rv); + + // Callback which adds a failure if it's ever called. + void FailingCompletionCallback(int rv); + + protected: + void Initialize(MockRead* reads, + size_t reads_count, + MockWrite* writes, + size_t writes_count); + + void AssertSyncReadEquals(const char* data, int len); + void AssertAsyncReadEquals(const char* data, int len); + void AssertReadReturns(int len, int rv); + void AssertReadBufferEquals(const char* data, int len); + + void AssertSyncWriteEquals(const char* data, int len); + void AssertAsyncWriteEquals(const char* data, int len); + void AssertWriteReturns(const char* data, int len, int rv); + + // When a given test completes, data_.at_eof() is expected to + // match the value specified here. Most test should consume all + // reads and writes, but some tests verify error handling behavior + // do not consume all data. + void set_expect_eof(bool expect_eof) { expect_eof_ = expect_eof; } + + TestCompletionCallback read_callback_; + scoped_refptr<IOBuffer> read_buf_; + TestCompletionCallback write_callback_; + CompletionCallback failing_callback_; + StreamSocket* sock_; + + private: + MockConnect connect_data_; + scoped_ptr<SequencedSocketData> data_; + + const HostPortPair endpoint_; + scoped_refptr<TransportSocketParams> tcp_params_; + MockClientSocketFactory socket_factory_; + MockTransportClientSocketPool socket_pool_; + ClientSocketHandle connection_; + bool expect_eof_; + + DISALLOW_COPY_AND_ASSIGN(SequencedSocketDataTest); +}; + +SequencedSocketDataTest::SequencedSocketDataTest() + : failing_callback_( + base::Bind(&SequencedSocketDataTest::FailingCompletionCallback, + base::Unretained(this))), + sock_(nullptr), + connect_data_(SYNCHRONOUS, OK), + endpoint_("www.google.com", 443), + tcp_params_(new TransportSocketParams( + endpoint_, + false, + false, + OnHostResolutionCallback(), + TransportSocketParams::COMBINE_CONNECT_AND_WRITE_DEFAULT)), + socket_pool_(10, 10, &socket_factory_), + expect_eof_(true) { +} + +SequencedSocketDataTest::~SequencedSocketDataTest() { + // Make sure no unexpected pending tasks will cause a failure. + base::RunLoop().RunUntilIdle(); + if (expect_eof_) { + EXPECT_EQ(expect_eof_, data_->at_read_eof()); + EXPECT_EQ(expect_eof_, data_->at_write_eof()); + } +} + +void SequencedSocketDataTest::Initialize(MockRead* reads, + size_t reads_count, + MockWrite* writes, + size_t writes_count) { + data_.reset( + new SequencedSocketData(reads, reads_count, writes, writes_count)); + data_->set_connect_data(connect_data_); + socket_factory_.AddSocketDataProvider(data_.get()); + + EXPECT_EQ(OK, + connection_.Init( + endpoint_.ToString(), tcp_params_, LOWEST, CompletionCallback(), + reinterpret_cast<TransportClientSocketPool*>(&socket_pool_), + BoundNetLog())); + sock_ = connection_.socket(); +} + +void SequencedSocketDataTest::AssertSyncReadEquals(const char* data, int len) { + // Issue the read, which will complete immediately. + AssertReadReturns(len, len); + AssertReadBufferEquals(data, len); +} + +void SequencedSocketDataTest::AssertAsyncReadEquals(const char* data, int len) { + // Issue the read, which will be completed asynchronously. + AssertReadReturns(len, ERR_IO_PENDING); + + EXPECT_TRUE(sock_->IsConnected()); + + // Now the read should complete. + ASSERT_EQ(len, read_callback_.WaitForResult()); + AssertReadBufferEquals(data, len); +} + +void SequencedSocketDataTest::AssertReadReturns(int len, int rv) { + read_buf_ = new IOBuffer(len); + if (rv == ERR_IO_PENDING) { + ASSERT_EQ(rv, sock_->Read(read_buf_.get(), len, read_callback_.callback())); + ASSERT_FALSE(read_callback_.have_result()); + } else { + ASSERT_EQ(rv, sock_->Read(read_buf_.get(), len, failing_callback_)); + } +} + +void SequencedSocketDataTest::AssertReadBufferEquals(const char* data, + int len) { + ASSERT_EQ(std::string(data, len), std::string(read_buf_->data(), len)); +} + +void SequencedSocketDataTest::AssertSyncWriteEquals(const char* data, int len) { + // Issue the write, which should be complete immediately. + AssertWriteReturns(data, len, len); + ASSERT_FALSE(write_callback_.have_result()); +} + +void SequencedSocketDataTest::AssertAsyncWriteEquals(const char* data, + int len) { + // Issue the read, which should be completed asynchronously. + AssertWriteReturns(data, len, ERR_IO_PENDING); + + EXPECT_FALSE(read_callback_.have_result()); + EXPECT_TRUE(sock_->IsConnected()); + + ASSERT_EQ(len, write_callback_.WaitForResult()); +} + +void SequencedSocketDataTest::AssertWriteReturns(const char* data, + int len, + int rv) { + scoped_refptr<IOBuffer> buf(new IOBuffer(len)); + memcpy(buf->data(), data, len); + + if (rv == ERR_IO_PENDING) { + ASSERT_EQ(rv, sock_->Write(buf.get(), len, write_callback_.callback())); + ASSERT_FALSE(write_callback_.have_result()); + } else { + ASSERT_EQ(rv, sock_->Write(buf.get(), len, failing_callback_)); + } +} + +void SequencedSocketDataTest::ReentrantReadCallback(const char* data, + int len1, + int len2, + int expected_rv2, + int rv) { + ASSERT_EQ(len1, rv); + AssertReadBufferEquals(data, len1); + + AssertReadReturns(len2, expected_rv2); +} + +void SequencedSocketDataTest::ReentrantAsyncReadCallback(int expected_rv, + int len, + int rv) { + ASSERT_EQ(expected_rv, rv); + + AssertReadReturns(len, ERR_IO_PENDING); +} + +void SequencedSocketDataTest::ReentrantWriteCallback(int expected_rv1, + const char* data, + int len, + int expected_rv2, + int rv) { + ASSERT_EQ(expected_rv1, rv); + + AssertWriteReturns(data, len, expected_rv2); +} + +void SequencedSocketDataTest::ReentrantAsyncWriteCallback( + const char* data, + int len, + CompletionCallback callback, + int expected_rv, + int rv) { + EXPECT_EQ(expected_rv, rv); + scoped_refptr<IOBuffer> write_buf(new IOBuffer(len)); + memcpy(write_buf->data(), data, len); + EXPECT_EQ(ERR_IO_PENDING, sock_->Write(write_buf.get(), len, callback)); +} + +void SequencedSocketDataTest::FailingCompletionCallback(int rv) { + ADD_FAILURE() << "Callback should not have been invoked"; +} + +// ----------- Read + +TEST_F(SequencedSocketDataTest, SingleSyncRead) { + MockRead reads[] = { + MockRead(SYNCHRONOUS, kMsg1, kLen1, 0), + }; + + Initialize(reads, arraysize(reads), nullptr, 0); + AssertSyncReadEquals(kMsg1, kLen1); +} + +TEST_F(SequencedSocketDataTest, MultipleSyncReads) { + MockRead reads[] = { + MockRead(SYNCHRONOUS, kMsg1, kLen1, 0), + MockRead(SYNCHRONOUS, kMsg2, kLen2, 1), + MockRead(SYNCHRONOUS, kMsg3, kLen3, 2), + MockRead(SYNCHRONOUS, kMsg3, kLen3, 3), + MockRead(SYNCHRONOUS, kMsg2, kLen2, 4), + MockRead(SYNCHRONOUS, kMsg3, kLen3, 5), + MockRead(SYNCHRONOUS, kMsg1, kLen1, 6), + }; + + Initialize(reads, arraysize(reads), nullptr, 0); + + AssertSyncReadEquals(kMsg1, kLen1); + AssertSyncReadEquals(kMsg2, kLen2); + AssertSyncReadEquals(kMsg3, kLen3); + AssertSyncReadEquals(kMsg3, kLen3); + AssertSyncReadEquals(kMsg2, kLen2); + AssertSyncReadEquals(kMsg3, kLen3); + AssertSyncReadEquals(kMsg1, kLen1); +} + +TEST_F(SequencedSocketDataTest, SingleAsyncRead) { + MockRead reads[] = { + MockRead(ASYNC, kMsg1, kLen1, 0), + }; + + Initialize(reads, arraysize(reads), nullptr, 0); + + AssertAsyncReadEquals(kMsg1, kLen1); +} + +TEST_F(SequencedSocketDataTest, MultipleAsyncReads) { + MockRead reads[] = { + MockRead(ASYNC, kMsg1, kLen1, 0), + MockRead(ASYNC, kMsg2, kLen2, 1), + MockRead(ASYNC, kMsg3, kLen3, 2), + MockRead(ASYNC, kMsg3, kLen3, 3), + MockRead(ASYNC, kMsg2, kLen2, 4), + MockRead(ASYNC, kMsg3, kLen3, 5), + MockRead(ASYNC, kMsg1, kLen1, 6), + }; + + Initialize(reads, arraysize(reads), nullptr, 0); + + AssertAsyncReadEquals(kMsg1, kLen1); + AssertAsyncReadEquals(kMsg2, kLen2); + AssertAsyncReadEquals(kMsg3, kLen3); + AssertAsyncReadEquals(kMsg3, kLen3); + AssertAsyncReadEquals(kMsg2, kLen2); + AssertAsyncReadEquals(kMsg3, kLen3); + AssertAsyncReadEquals(kMsg1, kLen1); +} + +TEST_F(SequencedSocketDataTest, MixedReads) { + MockRead reads[] = { + MockRead(SYNCHRONOUS, kMsg1, kLen1, 0), + MockRead(ASYNC, kMsg2, kLen2, 1), + MockRead(SYNCHRONOUS, kMsg3, kLen3, 2), + MockRead(ASYNC, kMsg3, kLen3, 3), + MockRead(SYNCHRONOUS, kMsg2, kLen2, 4), + MockRead(ASYNC, kMsg3, kLen3, 5), + MockRead(SYNCHRONOUS, kMsg1, kLen1, 6), + }; + + Initialize(reads, arraysize(reads), nullptr, 0); + + AssertSyncReadEquals(kMsg1, kLen1); + AssertAsyncReadEquals(kMsg2, kLen2); + AssertSyncReadEquals(kMsg3, kLen3); + AssertAsyncReadEquals(kMsg3, kLen3); + AssertSyncReadEquals(kMsg2, kLen2); + AssertAsyncReadEquals(kMsg3, kLen3); + AssertSyncReadEquals(kMsg1, kLen1); +} + +TEST_F(SequencedSocketDataTest, SyncReadFromCompletionCallback) { + MockRead reads[] = { + MockRead(ASYNC, kMsg1, kLen1, 0), MockRead(SYNCHRONOUS, kMsg2, kLen2, 1), + }; + + Initialize(reads, arraysize(reads), nullptr, 0); + + read_buf_ = new IOBuffer(kLen1); + ASSERT_EQ( + ERR_IO_PENDING, + sock_->Read( + read_buf_.get(), kLen1, + base::Bind(&SequencedSocketDataTest::ReentrantReadCallback, + base::Unretained(this), kMsg1, kLen1, kLen2, kLen2))); + + base::MessageLoop::current()->RunUntilIdle(); + AssertReadBufferEquals(kMsg2, kLen2); +} + +TEST_F(SequencedSocketDataTest, ManyReentrantReads) { + MockRead reads[] = { + MockRead(ASYNC, kMsg1, kLen1, 0), + MockRead(ASYNC, kMsg2, kLen2, 1), + MockRead(ASYNC, kMsg3, kLen3, 2), + MockRead(ASYNC, kMsg4, kLen4, 3), + }; + + Initialize(reads, arraysize(reads), nullptr, 0); + + read_buf_ = new IOBuffer(kLen4); + + ReentrantHelper helper3(sock_); + helper3.SetExpectedRead(kMsg3, kLen3); + helper3.SetInvokeRead(read_buf_, kLen4, ERR_IO_PENDING, + read_callback_.callback()); + + ReentrantHelper helper2(sock_); + helper2.SetExpectedRead(kMsg2, kLen2); + helper2.SetInvokeRead(helper3.read_buf(), kLen3, ERR_IO_PENDING, + helper3.callback()); + + ReentrantHelper helper(sock_); + helper.SetExpectedRead(kMsg1, kLen1); + helper.SetInvokeRead(helper2.read_buf(), kLen2, ERR_IO_PENDING, + helper2.callback()); + + sock_->Read(helper.read_buf().get(), kLen1, helper.callback()); + + ASSERT_EQ(kLen4, read_callback_.WaitForResult()); + AssertReadBufferEquals(kMsg4, kLen4); +} + +TEST_F(SequencedSocketDataTest, AsyncReadFromCompletionCallback) { + MockRead reads[] = { + MockRead(ASYNC, kMsg1, kLen1, 0), MockRead(ASYNC, kMsg2, kLen2, 1), + }; + + Initialize(reads, arraysize(reads), nullptr, 0); + + read_buf_ = new IOBuffer(kLen1); + ASSERT_EQ( + ERR_IO_PENDING, + sock_->Read(read_buf_.get(), kLen1, + base::Bind(&SequencedSocketDataTest::ReentrantReadCallback, + base::Unretained(this), kMsg1, kLen1, kLen2, + ERR_IO_PENDING))); + + ASSERT_FALSE(read_callback_.have_result()); + ASSERT_EQ(kLen2, read_callback_.WaitForResult()); + AssertReadBufferEquals(kMsg2, kLen2); +} + +TEST_F(SequencedSocketDataTest, SingleSyncReadTooEarly) { + MockRead reads[] = { + MockRead(SYNCHRONOUS, kMsg1, kLen1, 1), + }; + + MockWrite writes[] = {MockWrite(SYNCHRONOUS, 0, 0)}; + + Initialize(reads, arraysize(reads), writes, arraysize(writes)); + + EXPECT_NONFATAL_FAILURE(AssertReadReturns(kLen1, ERR_UNEXPECTED), + "Unable to perform synchronous IO while stopped"); + set_expect_eof(false); +} + +TEST_F(SequencedSocketDataTest, SingleSyncReadSmallBuffer) { + MockRead reads[] = { + MockRead(SYNCHRONOUS, kMsg1, kLen1, 0), + }; + + Initialize(reads, arraysize(reads), nullptr, 0); + + // Read the first chunk. + AssertReadReturns(kLen1 - 1, kLen1 - 1); + AssertReadBufferEquals(kMsg1, kLen1 - 1); + // Then read the second chunk. + AssertReadReturns(1, 1); + AssertReadBufferEquals(kMsg1 + kLen1 - 1, 1); +} + +TEST_F(SequencedSocketDataTest, SingleSyncReadLargeBuffer) { + MockRead reads[] = { + MockRead(SYNCHRONOUS, kMsg1, kLen1, 0), + }; + + Initialize(reads, arraysize(reads), nullptr, 0); + scoped_refptr<IOBuffer> read_buf(new IOBuffer(2 * kLen1)); + ASSERT_EQ(kLen1, sock_->Read(read_buf.get(), 2 * kLen1, failing_callback_)); + ASSERT_EQ(std::string(kMsg1, kLen1), std::string(read_buf->data(), kLen1)); +} + +TEST_F(SequencedSocketDataTest, SingleAsyncReadLargeBuffer) { + MockRead reads[] = { + MockRead(ASYNC, kMsg1, kLen1, 0), + }; + + Initialize(reads, arraysize(reads), nullptr, 0); + + scoped_refptr<IOBuffer> read_buf(new IOBuffer(2 * kLen1)); + ASSERT_EQ(ERR_IO_PENDING, + sock_->Read(read_buf.get(), 2 * kLen1, read_callback_.callback())); + ASSERT_EQ(kLen1, read_callback_.WaitForResult()); + ASSERT_EQ(std::string(kMsg1, kLen1), std::string(read_buf->data(), kLen1)); +} + +// ----------- Write + +TEST_F(SequencedSocketDataTest, SingleSyncWriteTooEarly) { + MockWrite writes[] = { + MockWrite(SYNCHRONOUS, kMsg1, kLen1, 1), + }; + + MockRead reads[] = {MockRead(SYNCHRONOUS, 0, 0)}; + + Initialize(reads, arraysize(reads), writes, arraysize(writes)); + + EXPECT_NONFATAL_FAILURE(AssertWriteReturns(kMsg1, kLen1, ERR_UNEXPECTED), + "Unable to perform synchronous IO while stopped"); + + set_expect_eof(false); +} + +TEST_F(SequencedSocketDataTest, DISABLED_SingleSyncWriteTooSmall) { + MockWrite writes[] = { + MockWrite(SYNCHRONOUS, kMsg1, kLen1, 0), + }; + + Initialize(nullptr, 0, writes, arraysize(writes)); + + // Attempt to write all of the message, but only some will be written. + EXPECT_NONFATAL_FAILURE(AssertSyncWriteEquals(kMsg1, kLen1 - 1), ""); +} + +TEST_F(SequencedSocketDataTest, SingleSyncPartialWrite) { + MockWrite writes[] = { + MockWrite(SYNCHRONOUS, kMsg1, kLen1 - 1, 0), + MockWrite(SYNCHRONOUS, kMsg1 + kLen1 - 1, 1, 1), + }; + + Initialize(nullptr, 0, writes, arraysize(writes)); + + // Attempt to write all of the message, but only some will be written. + AssertSyncWriteEquals(kMsg1, kLen1 - 1); + // Write the rest of the message. + AssertSyncWriteEquals(kMsg1 + kLen1 - 1, 1); +} + +TEST_F(SequencedSocketDataTest, SingleSyncWrite) { + MockWrite writes[] = { + MockWrite(SYNCHRONOUS, kMsg1, kLen1, 0), + }; + + Initialize(nullptr, 0, writes, arraysize(writes)); + + AssertSyncWriteEquals(kMsg1, kLen1); +} + +TEST_F(SequencedSocketDataTest, MultipleSyncWrites) { + MockWrite writes[] = { + MockWrite(SYNCHRONOUS, kMsg1, kLen1, 0), + MockWrite(SYNCHRONOUS, kMsg2, kLen2, 1), + MockWrite(SYNCHRONOUS, kMsg3, kLen3, 2), + MockWrite(SYNCHRONOUS, kMsg3, kLen3, 3), + MockWrite(SYNCHRONOUS, kMsg2, kLen2, 4), + MockWrite(SYNCHRONOUS, kMsg3, kLen3, 5), + MockWrite(SYNCHRONOUS, kMsg1, kLen1, 6), + }; + + Initialize(nullptr, 0, writes, arraysize(writes)); + + AssertSyncWriteEquals(kMsg1, kLen1); + AssertSyncWriteEquals(kMsg2, kLen2); + AssertSyncWriteEquals(kMsg3, kLen3); + AssertSyncWriteEquals(kMsg3, kLen3); + AssertSyncWriteEquals(kMsg2, kLen2); + AssertSyncWriteEquals(kMsg3, kLen3); + AssertSyncWriteEquals(kMsg1, kLen1); +} + +TEST_F(SequencedSocketDataTest, SingleAsyncWrite) { + MockWrite writes[] = { + MockWrite(ASYNC, kMsg1, kLen1, 0), + }; + + Initialize(nullptr, 0, writes, arraysize(writes)); + + AssertAsyncWriteEquals(kMsg1, kLen1); +} + +TEST_F(SequencedSocketDataTest, MultipleAsyncWrites) { + MockWrite writes[] = { + MockWrite(ASYNC, kMsg1, kLen1, 0), + MockWrite(ASYNC, kMsg2, kLen2, 1), + MockWrite(ASYNC, kMsg3, kLen3, 2), + MockWrite(ASYNC, kMsg3, kLen3, 3), + MockWrite(ASYNC, kMsg2, kLen2, 4), + MockWrite(ASYNC, kMsg3, kLen3, 5), + MockWrite(ASYNC, kMsg1, kLen1, 6), + }; + + Initialize(nullptr, 0, writes, arraysize(writes)); + + AssertAsyncWriteEquals(kMsg1, kLen1); + AssertAsyncWriteEquals(kMsg2, kLen2); + AssertAsyncWriteEquals(kMsg3, kLen3); + AssertAsyncWriteEquals(kMsg3, kLen3); + AssertAsyncWriteEquals(kMsg2, kLen2); + AssertAsyncWriteEquals(kMsg3, kLen3); + AssertAsyncWriteEquals(kMsg1, kLen1); +} + +TEST_F(SequencedSocketDataTest, MixedWrites) { + MockWrite writes[] = { + MockWrite(SYNCHRONOUS, kMsg1, kLen1, 0), + MockWrite(ASYNC, kMsg2, kLen2, 1), + MockWrite(SYNCHRONOUS, kMsg3, kLen3, 2), + MockWrite(ASYNC, kMsg3, kLen3, 3), + MockWrite(SYNCHRONOUS, kMsg2, kLen2, 4), + MockWrite(ASYNC, kMsg3, kLen3, 5), + MockWrite(SYNCHRONOUS, kMsg1, kLen1, 6), + }; + + Initialize(nullptr, 0, writes, arraysize(writes)); + + AssertSyncWriteEquals(kMsg1, kLen1); + AssertAsyncWriteEquals(kMsg2, kLen2); + AssertSyncWriteEquals(kMsg3, kLen3); + AssertAsyncWriteEquals(kMsg3, kLen3); + AssertSyncWriteEquals(kMsg2, kLen2); + AssertAsyncWriteEquals(kMsg3, kLen3); + AssertSyncWriteEquals(kMsg1, kLen1); +} + +TEST_F(SequencedSocketDataTest, SyncWriteFromCompletionCallback) { + MockWrite writes[] = { + MockWrite(ASYNC, kMsg1, kLen1, 0), + MockWrite(SYNCHRONOUS, kMsg2, kLen2, 1), + }; + + Initialize(nullptr, 0, writes, arraysize(writes)); + + scoped_refptr<IOBuffer> write_buf(new IOBuffer(kLen1)); + memcpy(write_buf->data(), kMsg1, kLen1); + ASSERT_EQ( + ERR_IO_PENDING, + sock_->Write( + write_buf.get(), kLen1, + base::Bind(&SequencedSocketDataTest::ReentrantWriteCallback, + base::Unretained(this), kLen1, kMsg2, kLen2, kLen2))); + + base::MessageLoop::current()->RunUntilIdle(); +} + +TEST_F(SequencedSocketDataTest, AsyncWriteFromCompletionCallback) { + MockWrite writes[] = { + MockWrite(ASYNC, kMsg1, kLen1, 0), MockWrite(ASYNC, kMsg2, kLen2, 1), + }; + + Initialize(nullptr, 0, writes, arraysize(writes)); + + scoped_refptr<IOBuffer> write_buf(new IOBuffer(kLen1)); + memcpy(write_buf->data(), kMsg1, kLen1); + ASSERT_EQ( + ERR_IO_PENDING, + sock_->Write(write_buf.get(), kLen1, + base::Bind(&SequencedSocketDataTest::ReentrantWriteCallback, + base::Unretained(this), kLen1, kMsg2, kLen2, + ERR_IO_PENDING))); + + ASSERT_FALSE(write_callback_.have_result()); + ASSERT_EQ(kLen2, write_callback_.WaitForResult()); +} + +TEST_F(SequencedSocketDataTest, ManyReentrantWrites) { + MockWrite writes[] = { + MockWrite(ASYNC, kMsg1, kLen1, 0), + MockWrite(ASYNC, kMsg2, kLen2, 1), + MockWrite(ASYNC, kMsg3, kLen3, 2), + MockWrite(ASYNC, kMsg4, kLen4, 3), + }; + + Initialize(nullptr, 0, writes, arraysize(writes)); + + ReentrantHelper helper3(sock_); + helper3.SetExpectedWrite(kLen3); + helper3.SetInvokeWrite(kMsg4, kLen4, ERR_IO_PENDING, + write_callback_.callback()); + + ReentrantHelper helper2(sock_); + helper2.SetExpectedWrite(kLen2); + helper2.SetInvokeWrite(kMsg3, kLen3, ERR_IO_PENDING, helper3.callback()); + + ReentrantHelper helper(sock_); + helper.SetExpectedWrite(kLen1); + helper.SetInvokeWrite(kMsg2, kLen2, ERR_IO_PENDING, helper2.callback()); + + scoped_refptr<IOBuffer> write_buf(new IOBuffer(kLen1)); + memcpy(write_buf->data(), kMsg1, kLen1); + sock_->Write(write_buf.get(), kLen1, helper.callback()); + + ASSERT_EQ(kLen4, write_callback_.WaitForResult()); +} + +// ----------- Mixed Reads and Writes + +TEST_F(SequencedSocketDataTest, MixedSyncOperations) { + MockRead reads[] = { + MockRead(SYNCHRONOUS, kMsg1, kLen1, 0), + MockRead(SYNCHRONOUS, kMsg2, kLen2, 3), + }; + + MockWrite writes[] = { + MockWrite(SYNCHRONOUS, kMsg2, kLen2, 1), + MockWrite(SYNCHRONOUS, kMsg3, kLen3, 2), + }; + + Initialize(reads, arraysize(reads), writes, arraysize(writes)); + + AssertSyncReadEquals(kMsg1, kLen1); + AssertSyncWriteEquals(kMsg2, kLen2); + AssertSyncWriteEquals(kMsg3, kLen3); + AssertSyncReadEquals(kMsg2, kLen2); +} + +TEST_F(SequencedSocketDataTest, MixedAsyncOperations) { + MockRead reads[] = { + MockRead(ASYNC, kMsg1, kLen1, 0), MockRead(ASYNC, kMsg2, kLen2, 3), + }; + + MockWrite writes[] = { + MockWrite(ASYNC, kMsg2, kLen2, 1), MockWrite(ASYNC, kMsg3, kLen3, 2), + }; + + Initialize(reads, arraysize(reads), writes, arraysize(writes)); + + AssertAsyncReadEquals(kMsg1, kLen1); + AssertAsyncWriteEquals(kMsg2, kLen2); + AssertAsyncWriteEquals(kMsg3, kLen3); + AssertAsyncReadEquals(kMsg2, kLen2); +} + +TEST_F(SequencedSocketDataTest, InterleavedAsyncOperations) { + // Order of completion is read, write, write, read. + MockRead reads[] = { + MockRead(ASYNC, kMsg1, kLen1, 0), MockRead(ASYNC, kMsg2, kLen2, 3), + }; + + MockWrite writes[] = { + MockWrite(ASYNC, kMsg2, kLen2, 1), MockWrite(ASYNC, kMsg3, kLen3, 2), + }; + + Initialize(reads, arraysize(reads), writes, arraysize(writes)); + + // Issue the write, which will block until the read completes. + AssertWriteReturns(kMsg2, kLen2, ERR_IO_PENDING); + + // Issue the read which will return first. + AssertReadReturns(kLen1, ERR_IO_PENDING); + + ASSERT_EQ(kLen1, read_callback_.WaitForResult()); + AssertReadBufferEquals(kMsg1, kLen1); + + ASSERT_TRUE(write_callback_.have_result()); + ASSERT_EQ(kLen2, write_callback_.WaitForResult()); + + // Issue the read, which will block until the write completes. + AssertReadReturns(kLen2, ERR_IO_PENDING); + + // Issue the writes which will return first. + AssertWriteReturns(kMsg3, kLen3, ERR_IO_PENDING); + ASSERT_EQ(kLen3, write_callback_.WaitForResult()); + + ASSERT_EQ(kLen2, read_callback_.WaitForResult()); + AssertReadBufferEquals(kMsg2, kLen2); +} + +TEST_F(SequencedSocketDataTest, InterleavedMixedOperations) { + // Order of completion is read, write, write, read. + MockRead reads[] = { + MockRead(SYNCHRONOUS, kMsg1, kLen1, 0), + MockRead(ASYNC, kMsg2, kLen2, 3), + MockRead(ASYNC, kMsg3, kLen3, 5), + }; + + MockWrite writes[] = { + MockWrite(ASYNC, kMsg2, kLen2, 1), + MockWrite(SYNCHRONOUS, kMsg3, kLen3, 2), + MockWrite(SYNCHRONOUS, kMsg1, kLen1, 4), + }; + + Initialize(reads, arraysize(reads), writes, arraysize(writes)); + + // Issue the write, which will block until the read completes. + AssertWriteReturns(kMsg2, kLen2, ERR_IO_PENDING); + + // Issue the writes which will complete immediately. + AssertSyncReadEquals(kMsg1, kLen1); + + ASSERT_FALSE(write_callback_.have_result()); + ASSERT_EQ(kLen2, write_callback_.WaitForResult()); + + // Issue the read, which will block until the write completes. + AssertReadReturns(kLen2, ERR_IO_PENDING); + + // Issue the writes which will complete immediately. + AssertSyncWriteEquals(kMsg3, kLen3); + + ASSERT_FALSE(read_callback_.have_result()); + ASSERT_EQ(kLen2, read_callback_.WaitForResult()); + AssertReadBufferEquals(kMsg2, kLen2); + + // Issue the read, which will block until the write completes. + AssertReadReturns(kLen2, ERR_IO_PENDING); + + // Issue the writes which will complete immediately. + AssertSyncWriteEquals(kMsg1, kLen1); + + ASSERT_FALSE(read_callback_.have_result()); + ASSERT_EQ(kLen3, read_callback_.WaitForResult()); + AssertReadBufferEquals(kMsg3, kLen3); +} + +TEST_F(SequencedSocketDataTest, AsyncReadFromWriteCompletionCallback) { + MockWrite writes[] = { + MockWrite(ASYNC, kMsg1, kLen1, 0), + }; + + MockRead reads[] = { + MockRead(ASYNC, kMsg2, kLen2, 1), + }; + + Initialize(reads, arraysize(reads), writes, arraysize(writes)); + + scoped_refptr<IOBuffer> write_buf(new IOBuffer(kLen1)); + memcpy(write_buf->data(), kMsg1, kLen1); + ASSERT_EQ(ERR_IO_PENDING, + sock_->Write( + write_buf.get(), kLen1, + base::Bind(&SequencedSocketDataTest::ReentrantAsyncReadCallback, + base::Unretained(this), kLen1, kLen2))); + + ASSERT_FALSE(read_callback_.have_result()); + ASSERT_EQ(kLen2, read_callback_.WaitForResult()); + AssertReadBufferEquals(kMsg2, kLen2); +} + +TEST_F(SequencedSocketDataTest, AsyncWriteFromReadCompletionCallback) { + MockWrite writes[] = { + MockWrite(ASYNC, kMsg2, kLen2, 1), + }; + + MockRead reads[] = { + MockRead(ASYNC, kMsg1, kLen1, 0), + }; + + Initialize(reads, arraysize(reads), writes, arraysize(writes)); + + scoped_refptr<IOBuffer> read_buf(new IOBuffer(kLen1)); + ASSERT_EQ( + ERR_IO_PENDING, + sock_->Read( + read_buf.get(), kLen1, + base::Bind(&SequencedSocketDataTest::ReentrantAsyncWriteCallback, + base::Unretained(this), kMsg2, kLen2, + write_callback_.callback(), kLen1))); + + ASSERT_FALSE(write_callback_.have_result()); + ASSERT_EQ(kLen2, write_callback_.WaitForResult()); +} + +TEST_F(SequencedSocketDataTest, MixedReentrantOperations) { + MockWrite writes[] = { + MockWrite(ASYNC, kMsg1, kLen1, 0), MockWrite(ASYNC, kMsg3, kLen3, 2), + }; + + MockRead reads[] = { + MockRead(ASYNC, kMsg2, kLen2, 1), MockRead(ASYNC, kMsg4, kLen4, 3), + }; + + Initialize(reads, arraysize(reads), writes, arraysize(writes)); + + read_buf_ = new IOBuffer(kLen4); + + ReentrantHelper helper3(sock_); + helper3.SetExpectedWrite(kLen3); + helper3.SetInvokeRead(read_buf_, kLen4, ERR_IO_PENDING, + read_callback_.callback()); + + ReentrantHelper helper2(sock_); + helper2.SetExpectedRead(kMsg2, kLen2); + helper2.SetInvokeWrite(kMsg3, kLen3, ERR_IO_PENDING, helper3.callback()); + + ReentrantHelper helper(sock_); + helper.SetExpectedWrite(kLen1); + helper.SetInvokeRead(helper2.read_buf(), kLen2, ERR_IO_PENDING, + helper2.callback()); + + scoped_refptr<IOBuffer> write_buf(new IOBuffer(kLen1)); + memcpy(write_buf->data(), kMsg1, kLen1); + sock_->Write(write_buf.get(), kLen1, helper.callback()); + + ASSERT_EQ(kLen4, read_callback_.WaitForResult()); +} + +TEST_F(SequencedSocketDataTest, MixedReentrantOperationsThenSynchronousRead) { + MockWrite writes[] = { + MockWrite(ASYNC, kMsg1, kLen1, 0), MockWrite(ASYNC, kMsg3, kLen3, 2), + }; + + MockRead reads[] = { + MockRead(ASYNC, kMsg2, kLen2, 1), MockRead(SYNCHRONOUS, kMsg4, kLen4, 3), + }; + + Initialize(reads, arraysize(reads), writes, arraysize(writes)); + + read_buf_ = new IOBuffer(kLen4); + + ReentrantHelper helper3(sock_); + helper3.SetExpectedWrite(kLen3); + helper3.SetInvokeRead(read_buf_, kLen4, kLen4, failing_callback_); + + ReentrantHelper helper2(sock_); + helper2.SetExpectedRead(kMsg2, kLen2); + helper2.SetInvokeWrite(kMsg3, kLen3, ERR_IO_PENDING, helper3.callback()); + + ReentrantHelper helper(sock_); + helper.SetExpectedWrite(kLen1); + helper.SetInvokeRead(helper2.read_buf(), kLen2, ERR_IO_PENDING, + helper2.callback()); + + scoped_refptr<IOBuffer> write_buf(new IOBuffer(kLen1)); + memcpy(write_buf->data(), kMsg1, kLen1); + ASSERT_EQ(ERR_IO_PENDING, + sock_->Write(write_buf.get(), kLen1, helper.callback())); + + base::MessageLoop::current()->RunUntilIdle(); + AssertReadBufferEquals(kMsg4, kLen4); +} + +TEST_F(SequencedSocketDataTest, MixedReentrantOperationsThenSynchronousWrite) { + MockWrite writes[] = { + MockWrite(ASYNC, kMsg2, kLen2, 1), + MockWrite(SYNCHRONOUS, kMsg4, kLen4, 3), + }; + + MockRead reads[] = { + MockRead(ASYNC, kMsg1, kLen1, 0), MockRead(ASYNC, kMsg3, kLen3, 2), + }; + + Initialize(reads, arraysize(reads), writes, arraysize(writes)); + + read_buf_ = new IOBuffer(kLen4); + + ReentrantHelper helper3(sock_); + helper3.SetExpectedRead(kMsg3, kLen3); + helper3.SetInvokeWrite(kMsg4, kLen4, kLen4, failing_callback_); + + ReentrantHelper helper2(sock_); + helper2.SetExpectedWrite(kLen2); + helper2.SetInvokeRead(helper3.read_buf(), kLen3, ERR_IO_PENDING, + helper3.callback()); + + ReentrantHelper helper(sock_); + helper.SetExpectedRead(kMsg1, kLen1); + helper.SetInvokeWrite(kMsg2, kLen2, ERR_IO_PENDING, helper2.callback()); + + ASSERT_EQ(ERR_IO_PENDING, + sock_->Read(helper.read_buf().get(), kLen1, helper.callback())); + + base::MessageLoop::current()->RunUntilIdle(); +} + +} // namespace + +} // namespace net
diff --git a/net/socket/socket_net_log_params.cc b/net/socket/socket_net_log_params.cc index bcc12c8..3dd6595 100644 --- a/net/socket/socket_net_log_params.cc +++ b/net/socket/socket_net_log_params.cc
@@ -16,7 +16,7 @@ base::Value* NetLogSocketErrorCallback(int net_error, int os_error, - NetLog::LogLevel /* log_level */) { + NetLogCaptureMode /* capture_mode */) { base::DictionaryValue* dict = new base::DictionaryValue(); dict->SetInteger("net_error", net_error); dict->SetInteger("os_error", os_error); @@ -24,14 +24,14 @@ } base::Value* NetLogHostPortPairCallback(const HostPortPair* host_and_port, - NetLog::LogLevel /* log_level */) { + NetLogCaptureMode /* capture_mode */) { base::DictionaryValue* dict = new base::DictionaryValue(); dict->SetString("host_and_port", host_and_port->ToString()); return dict; } base::Value* NetLogIPEndPointCallback(const IPEndPoint* address, - NetLog::LogLevel /* log_level */) { + NetLogCaptureMode /* capture_mode */) { base::DictionaryValue* dict = new base::DictionaryValue(); dict->SetString("address", address->ToString()); return dict; @@ -39,7 +39,7 @@ base::Value* NetLogSourceAddressCallback(const struct sockaddr* net_address, socklen_t address_len, - NetLog::LogLevel /* log_level */) { + NetLogCaptureMode /* capture_mode */) { base::DictionaryValue* dict = new base::DictionaryValue(); dict->SetString("source_address", NetAddressToStringWithPort(net_address, address_len));
diff --git a/net/socket/socket_test_util.cc b/net/socket/socket_test_util.cc index b905597..b3f5040e 100644 --- a/net/socket/socket_test_util.cc +++ b/net/socket/socket_test_util.cc
@@ -11,6 +11,7 @@ #include "base/bind.h" #include "base/bind_helpers.h" #include "base/compiler_specific.h" +#include "base/logging.h" #include "base/message_loop/message_loop.h" #include "base/run_loop.h" #include "base/time/time.h" @@ -174,6 +175,26 @@ return writes_[write_index_++]; } +bool StaticSocketDataHelper::VerifyWriteData(const std::string& data) { + CHECK(!at_write_eof()); + // Check that what the actual data matches the expectations. + const MockWrite& next_write = PeekWrite(); + if (!next_write.data) + return true; + + // Note: Partial writes are supported here. If the expected data + // is a match, but shorter than the write actually written, that is legal. + // Example: + // Application writes "foobarbaz" (9 bytes) + // Expected write was "foo" (3 bytes) + // This is a success, and the function returns true. + std::string expected_data(next_write.data, next_write.data_len); + std::string actual_data(data.substr(0, next_write.data_len)); + EXPECT_GE(data.length(), expected_data.length()); + EXPECT_EQ(expected_data, actual_data); + return expected_data == actual_data; +} + void StaticSocketDataHelper::Reset() { read_index_ = 0; write_index_ = 0; @@ -212,25 +233,15 @@ // Check that what we are writing matches the expectation. // Then give the mocked return value. - const MockWrite& w = helper_.AdvanceWrite(); - int result = w.result; - if (w.data) { - // Note - we can simulate a partial write here. If the expected data - // is a match, but shorter than the write actually written, that is legal. - // Example: - // Application writes "foobarbaz" (9 bytes) - // Expected write was "foo" (3 bytes) - // This is a success, and we return 3 to the application. - std::string expected_data(w.data, w.data_len); - EXPECT_GE(data.length(), expected_data.length()); - std::string actual_data(data.substr(0, w.data_len)); - EXPECT_EQ(expected_data, actual_data); - if (expected_data != actual_data) - return MockWriteResult(SYNCHRONOUS, ERR_UNEXPECTED); - if (result == OK) - result = w.data_len; - } - return MockWriteResult(w.mode, result); + if (!helper_.VerifyWriteData(data)) + return MockWriteResult(SYNCHRONOUS, ERR_UNEXPECTED); + + const MockWrite& next_write = helper_.AdvanceWrite(); + // In the case that the write was successful, return the number of bytes + // written. Otherwise return the error code. + int result = + next_write.result == OK ? next_write.data_len : next_write.result; + return MockWriteResult(next_write.mode, result); } void StaticSocketDataProvider::Reset() { @@ -454,6 +465,235 @@ OrderedSocketData::~OrderedSocketData() {} +SequencedSocketData::SequencedSocketData(MockRead* reads, + size_t reads_count, + MockWrite* writes, + size_t writes_count) + : helper_(reads, reads_count, writes, writes_count), + sequence_number_(0), + read_state_(IDLE), + write_state_(IDLE), + weak_factory_(this) { + // Check that reads and writes have a contiguous set of sequence numbers + // starting from 0 and working their way up, with no repeats and skipping + // no values. + size_t next_read = 0; + size_t next_write = 0; + int next_sequence_number = 0; + while (next_read < reads_count || next_write < writes_count) { + if (next_read < reads_count && + reads[next_read].sequence_number == next_sequence_number) { + ++next_read; + ++next_sequence_number; + continue; + } + if (next_write < writes_count && + writes[next_write].sequence_number == next_sequence_number) { + ++next_write; + ++next_sequence_number; + continue; + } + CHECK(false) << "Sequence number not found where expected: " + << next_sequence_number; + return; + } + CHECK_EQ(next_read, reads_count); + CHECK_EQ(next_write, writes_count); +} + +SequencedSocketData::SequencedSocketData(const MockConnect& connect, + MockRead* reads, + size_t reads_count, + MockWrite* writes, + size_t writes_count) + : SequencedSocketData(reads, reads_count, writes, writes_count) { + set_connect_data(connect); +} + +MockRead SequencedSocketData::OnRead() { + CHECK_EQ(IDLE, read_state_); + CHECK(!helper_.at_read_eof()); + + NET_TRACE(1, " *** ") << "sequence_number: " << sequence_number_; + const MockRead& next_read = helper_.PeekRead(); + NET_TRACE(1, " *** ") << "next_read: " << next_read.sequence_number; + CHECK_GE(next_read.sequence_number, sequence_number_); + + if (next_read.sequence_number <= sequence_number_) { + if (next_read.mode == SYNCHRONOUS) { + NET_TRACE(1, " *** ") << "Returning synchronously"; + DumpMockReadWrite(next_read); + helper_.AdvanceRead(); + ++sequence_number_; + MaybePostWriteCompleteTask(); + return next_read; + } + + base::MessageLoop::current()->PostTask( + FROM_HERE, base::Bind(&SequencedSocketData::OnReadComplete, + weak_factory_.GetWeakPtr())); + CHECK_NE(COMPLETING, write_state_); + read_state_ = COMPLETING; + } else if (next_read.mode == SYNCHRONOUS) { + ADD_FAILURE() << "Unable to perform synchronous IO while stopped"; + return MockRead(SYNCHRONOUS, ERR_UNEXPECTED); + } else { + NET_TRACE(1, " *** ") << "Waiting for write to trigger read"; + read_state_ = PENDING; + } + + return MockRead(SYNCHRONOUS, ERR_IO_PENDING); +} + +MockWriteResult SequencedSocketData::OnWrite(const std::string& data) { + CHECK_EQ(IDLE, write_state_); + CHECK(!helper_.at_write_eof()); + + NET_TRACE(1, " *** ") << "sequence_number: " << sequence_number_; + const MockWrite& next_write = helper_.PeekWrite(); + NET_TRACE(1, " *** ") << "next_write: " << next_write.sequence_number; + CHECK_GE(next_write.sequence_number, sequence_number_); + + if (!helper_.VerifyWriteData(data)) + return MockWriteResult(SYNCHRONOUS, ERR_UNEXPECTED); + + if (next_write.sequence_number <= sequence_number_) { + if (next_write.mode == SYNCHRONOUS) { + helper_.AdvanceWrite(); + ++sequence_number_; + MaybePostReadCompleteTask(); + // In the case that the write was successful, return the number of bytes + // written. Otherwise return the error code. + int rv = + next_write.result != OK ? next_write.result : next_write.data_len; + NET_TRACE(1, " *** ") << "Returning synchronously"; + return MockWriteResult(SYNCHRONOUS, rv); + } + + NET_TRACE(1, " *** ") << "Posting task to complete write"; + base::MessageLoop::current()->PostTask( + FROM_HERE, base::Bind(&SequencedSocketData::OnWriteComplete, + weak_factory_.GetWeakPtr())); + CHECK_NE(COMPLETING, read_state_); + write_state_ = COMPLETING; + } else if (next_write.mode == SYNCHRONOUS) { + ADD_FAILURE() << "Unable to perform synchronous IO while stopped"; + return MockWriteResult(SYNCHRONOUS, ERR_UNEXPECTED); + } else { + NET_TRACE(1, " *** ") << "Waiting for read to trigger write"; + write_state_ = PENDING; + } + + return MockWriteResult(SYNCHRONOUS, ERR_IO_PENDING); +} + +void SequencedSocketData::Reset() { + helper_.Reset(); + sequence_number_ = 0; + read_state_ = IDLE; + write_state_ = IDLE; + weak_factory_.InvalidateWeakPtrs(); +} + +bool SequencedSocketData::at_read_eof() { + return helper_.at_read_eof(); +} + +bool SequencedSocketData::at_write_eof() { + return helper_.at_read_eof(); +} + +void SequencedSocketData::MaybePostReadCompleteTask() { + NET_TRACE(1, " ****** ") << " current: " << sequence_number_; + // Only trigger the next read to complete if there is already a read pending + // which should complete at the current sequence number. + if (read_state_ != PENDING || + helper_.PeekRead().sequence_number != sequence_number_) { + return; + } + + NET_TRACE(1, " ****** ") << "Posting task to complete read: " + << sequence_number_; + base::MessageLoop::current()->PostTask( + FROM_HERE, base::Bind(&SequencedSocketData::OnReadComplete, + weak_factory_.GetWeakPtr())); + CHECK_NE(COMPLETING, write_state_); + read_state_ = COMPLETING; +} + +void SequencedSocketData::MaybePostWriteCompleteTask() { + NET_TRACE(1, " ****** ") << " current: " << sequence_number_; + // Only trigger the next write to complete if there is already a write pending + // which should complete at the current sequence number. + if (write_state_ != PENDING || + helper_.PeekWrite().sequence_number != sequence_number_) { + return; + } + + NET_TRACE(1, " ****** ") << "Posting task to complete write: " + << sequence_number_; + base::MessageLoop::current()->PostTask( + FROM_HERE, base::Bind(&SequencedSocketData::OnWriteComplete, + weak_factory_.GetWeakPtr())); + CHECK_NE(COMPLETING, read_state_); + write_state_ = COMPLETING; +} + +void SequencedSocketData::OnReadComplete() { + CHECK_EQ(COMPLETING, read_state_); + NET_TRACE(1, " *** ") << "Completing read for: " << sequence_number_; + if (!socket()) { + NET_TRACE(1, " *** ") << "No socket available to complete read"; + return; + } + + MockRead data = helper_.AdvanceRead(); + DCHECK_EQ(sequence_number_, data.sequence_number); + sequence_number_++; + read_state_ = IDLE; + + // The result of this read completing might trigger the completion + // of a pending write. If so, post a task to complete the write later. + // Since the socket may call back into the SequencedSocketData + // from socket()->OnReadComplete(), trigger the write task to be posted + // before calling that. + MaybePostWriteCompleteTask(); + + NET_TRACE(1, " *** ") << "Completing socket read for: " << sequence_number_; + DumpMockReadWrite(data); + socket()->OnReadComplete(data); + NET_TRACE(1, " *** ") << "Done"; +} + +void SequencedSocketData::OnWriteComplete() { + CHECK_EQ(COMPLETING, write_state_); + NET_TRACE(1, " *** ") << " Completing write for: " << sequence_number_; + if (!socket()) { + NET_TRACE(1, " *** ") << "No socket available to complete write."; + return; + } + + const MockWrite& data = helper_.AdvanceWrite(); + DCHECK_EQ(sequence_number_, data.sequence_number); + sequence_number_++; + write_state_ = IDLE; + int rv = data.result == OK ? data.data_len : data.result; + + // The result of this write completing might trigger the completion + // of a pending read. If so, post a task to complete the read later. + // Since the socket may call back into the SequencedSocketData + // from socket()->OnWriteComplete(), trigger the write task to be posted + // before calling that. + MaybePostReadCompleteTask(); + + NET_TRACE(1, " *** ") << " Completing socket write for: " << sequence_number_; + socket()->OnWriteComplete(rv); + NET_TRACE(1, " *** ") << "Done"; +} + +SequencedSocketData::~SequencedSocketData() { +} + DeterministicSocketData::DeterministicSocketData(MockRead* reads, size_t reads_count, MockWrite* writes, size_t writes_count) : StaticSocketDataProvider(reads, reads_count, writes, writes_count), @@ -824,8 +1064,8 @@ read_data_(SYNCHRONOUS, ERR_UNEXPECTED), need_read_data_(true), peer_closed_connection_(false), - pending_buf_(NULL), - pending_buf_len_(0), + pending_read_buf_(NULL), + pending_read_buf_len_(0), was_used_to_convey_data_(false) { DCHECK(data_); peer_addr_ = data->connect_data().peer_addr; @@ -840,12 +1080,12 @@ return ERR_UNEXPECTED; // If the buffer is already in use, a read is already in progress! - DCHECK(pending_buf_.get() == NULL); + DCHECK(pending_read_buf_.get() == NULL); // Store our async IO data. - pending_buf_ = buf; - pending_buf_len_ = buf_len; - pending_callback_ = callback; + pending_read_buf_ = buf; + pending_read_buf_len_ = buf_len; + pending_read_callback_ = callback; if (need_read_data_) { read_data_ = data_->OnRead(); @@ -886,6 +1126,15 @@ was_used_to_convey_data_ = true; + // ERR_IO_PENDING is a signal that the socket data will call back + // asynchronously later. + if (write_result.result == ERR_IO_PENDING) { + pending_write_callback_ = callback; + return ERR_IO_PENDING; + } + + // TODO(rch): remove this once OrderedSocketData and DelayedSocketData + // have been removed. if (write_result.mode == ASYNC) { RunCallbackAsync(callback, write_result.result); return ERR_IO_PENDING; @@ -901,7 +1150,7 @@ peer_closed_connection_ = false; if (data_->connect_data().mode == ASYNC) { if (data_->connect_data().result == ERR_IO_PENDING) - pending_callback_ = callback; + pending_read_callback_ = callback; else RunCallbackAsync(callback, data_->connect_data().result); return ERR_IO_PENDING; @@ -911,7 +1160,7 @@ void MockTCPClientSocket::Disconnect() { MockClientSocket::Disconnect(); - pending_callback_.Reset(); + pending_read_callback_.Reset(); } bool MockTCPClientSocket::IsConnected() const { @@ -948,7 +1197,7 @@ void MockTCPClientSocket::OnReadComplete(const MockRead& data) { // There must be a read pending. - DCHECK(pending_buf_.get()); + DCHECK(pending_read_buf_.get()); // You can't complete a read with another ERR_IO_PENDING status code. DCHECK_NE(ERR_IO_PENDING, data.result); // Since we've been waiting for data, need_read_data_ should be true. @@ -961,29 +1210,36 @@ // let CompleteRead() schedule a callback. read_data_.mode = SYNCHRONOUS; - CompletionCallback callback = pending_callback_; + CompletionCallback callback = pending_read_callback_; int rv = CompleteRead(); RunCallback(callback, rv); } +void MockTCPClientSocket::OnWriteComplete(int rv) { + // There must be a read pending. + DCHECK(!pending_write_callback_.is_null()); + CompletionCallback callback = pending_write_callback_; + RunCallback(callback, rv); +} + void MockTCPClientSocket::OnConnectComplete(const MockConnect& data) { - CompletionCallback callback = pending_callback_; + CompletionCallback callback = pending_read_callback_; RunCallback(callback, data.result); } int MockTCPClientSocket::CompleteRead() { - DCHECK(pending_buf_.get()); - DCHECK(pending_buf_len_ > 0); + DCHECK(pending_read_buf_.get()); + DCHECK(pending_read_buf_len_ > 0); was_used_to_convey_data_ = true; // Save the pending async IO data and reset our |pending_| state. - scoped_refptr<IOBuffer> buf = pending_buf_; - int buf_len = pending_buf_len_; - CompletionCallback callback = pending_callback_; - pending_buf_ = NULL; - pending_buf_len_ = 0; - pending_callback_.Reset(); + scoped_refptr<IOBuffer> buf = pending_read_buf_; + int buf_len = pending_read_buf_len_; + CompletionCallback callback = pending_read_callback_; + pending_read_buf_ = NULL; + pending_read_buf_len_ = 0; + pending_read_callback_.Reset(); int result = read_data_.result; DCHECK(result != ERR_IO_PENDING); @@ -1202,6 +1458,9 @@ void DeterministicMockUDPClientSocket::OnReadComplete(const MockRead& data) {} +void DeterministicMockUDPClientSocket::OnWriteComplete(int rv) { +} + void DeterministicMockUDPClientSocket::OnConnectComplete( const MockConnect& data) { NOTIMPLEMENTED(); @@ -1296,6 +1555,9 @@ void DeterministicMockTCPClientSocket::OnReadComplete(const MockRead& data) {} +void DeterministicMockTCPClientSocket::OnWriteComplete(int rv) { +} + void DeterministicMockTCPClientSocket::OnConnectComplete( const MockConnect& data) {} @@ -1445,6 +1707,10 @@ NOTIMPLEMENTED(); } +void MockSSLClientSocket::OnWriteComplete(int rv) { + NOTIMPLEMENTED(); +} + void MockSSLClientSocket::OnConnectComplete(const MockConnect& data) { NOTIMPLEMENTED(); } @@ -1457,8 +1723,8 @@ read_data_(SYNCHRONOUS, ERR_UNEXPECTED), need_read_data_(true), source_port_(123), - pending_buf_(NULL), - pending_buf_len_(0), + pending_read_buf_(NULL), + pending_read_buf_len_(0), net_log_(BoundNetLog::Make(net_log, net::NetLog::SOURCE_NONE)), weak_factory_(this) { DCHECK(data_); @@ -1475,12 +1741,12 @@ return ERR_UNEXPECTED; // If the buffer is already in use, a read is already in progress! - DCHECK(pending_buf_.get() == NULL); + DCHECK(pending_read_buf_.get() == NULL); // Store our async IO data. - pending_buf_ = buf; - pending_buf_len_ = buf_len; - pending_callback_ = callback; + pending_read_buf_ = buf; + pending_read_buf_len_ = buf_len; + pending_read_callback_ = callback; if (need_read_data_) { read_data_ = data_->OnRead(); @@ -1508,6 +1774,12 @@ std::string data(buf->data(), buf_len); MockWriteResult write_result = data_->OnWrite(data); + // ERR_IO_PENDING is a signal that the socket data will call back + // asynchronously. + if (write_result.result == ERR_IO_PENDING) { + pending_write_callback_ = callback; + return ERR_IO_PENDING; + } if (write_result.mode == ASYNC) { RunCallbackAsync(callback, write_result.result); return ERR_IO_PENDING; @@ -1552,7 +1824,7 @@ void MockUDPClientSocket::OnReadComplete(const MockRead& data) { // There must be a read pending. - DCHECK(pending_buf_.get()); + DCHECK(pending_read_buf_.get()); // You can't complete a read with another ERR_IO_PENDING status code. DCHECK_NE(ERR_IO_PENDING, data.result); // Since we've been waiting for data, need_read_data_ should be true. @@ -1565,26 +1837,33 @@ // let CompleteRead() schedule a callback. read_data_.mode = SYNCHRONOUS; - net::CompletionCallback callback = pending_callback_; + net::CompletionCallback callback = pending_read_callback_; int rv = CompleteRead(); RunCallback(callback, rv); } +void MockUDPClientSocket::OnWriteComplete(int rv) { + // There must be a read pending. + DCHECK(!pending_write_callback_.is_null()); + CompletionCallback callback = pending_write_callback_; + RunCallback(callback, rv); +} + void MockUDPClientSocket::OnConnectComplete(const MockConnect& data) { NOTIMPLEMENTED(); } int MockUDPClientSocket::CompleteRead() { - DCHECK(pending_buf_.get()); - DCHECK(pending_buf_len_ > 0); + DCHECK(pending_read_buf_.get()); + DCHECK(pending_read_buf_len_ > 0); // Save the pending async IO data and reset our |pending_| state. - scoped_refptr<IOBuffer> buf = pending_buf_; - int buf_len = pending_buf_len_; - CompletionCallback callback = pending_callback_; - pending_buf_ = NULL; - pending_buf_len_ = 0; - pending_callback_.Reset(); + scoped_refptr<IOBuffer> buf = pending_read_buf_; + int buf_len = pending_read_buf_len_; + CompletionCallback callback = pending_read_callback_; + pending_read_buf_ = NULL; + pending_read_buf_len_ = 0; + pending_read_callback_.Reset(); int result = read_data_.result; DCHECK(result != ERR_IO_PENDING);
diff --git a/net/socket/socket_test_util.h b/net/socket/socket_test_util.h index 1d99619..4dad762 100644 --- a/net/socket/socket_test_util.h +++ b/net/socket/socket_test_util.h
@@ -222,7 +222,13 @@ // is called to complete the asynchronous read operation. // data.async is ignored, and this read is completed synchronously as // part of this call. + // TODO(rch): this should take a StringPiece since most of the fields + // are ignored. virtual void OnReadComplete(const MockRead& data) = 0; + // If an async IO is pending because the SocketDataProvider returned + // ERR_IO_PENDING, then the AsyncSocket waits until this OnReadComplete + // is called to complete the asynchronous read operation. + virtual void OnWriteComplete(int rv) = 0; virtual void OnConnectComplete(const MockConnect& data) = 0; }; @@ -247,6 +253,11 @@ // Resets the read and write indexes to 0. void Reset(); + // Returns true if |data| is valid data for the next write. In order + // to support short writes, the next write may be longer than |data| + // in which case this method will still return true. + bool VerifyWriteData(const std::string& data); + size_t read_index() const { return read_index_; } size_t write_index() const { return write_index_; } size_t read_count() const { return read_count_; } @@ -464,6 +475,63 @@ DISALLOW_COPY_AND_ASSIGN(OrderedSocketData); }; +// Uses the sequence_number field in the mock reads and writes to +// complete the operations in a specified order. +class SequencedSocketData : public SocketDataProvider { + public: + // |reads| is the list of MockRead completions. + // |writes| is the list of MockWrite completions. + SequencedSocketData(MockRead* reads, + size_t reads_count, + MockWrite* writes, + size_t writes_count); + + // |connect| is the result for the connect phase. + // |reads| is the list of MockRead completions. + // |writes| is the list of MockWrite completions. + SequencedSocketData(const MockConnect& connect, + MockRead* reads, + size_t reads_count, + MockWrite* writes, + size_t writes_count); + + ~SequencedSocketData() override; + + // SocketDataProviderBase implementation. + MockRead OnRead() override; + MockWriteResult OnWrite(const std::string& data) override; + void Reset() override; + + // Returns true if all data has been read. + bool at_read_eof(); + + // Returns true if all data has been written. + bool at_write_eof(); + + private: + // Defines the state for the read or write path. + enum IoState { + IDLE, // No async operation is in progress. + PENDING, // An async operation in waiting for another opteration to + // complete. + COMPLETING, // A task has been posted to complet an async operation. + }; + void OnReadComplete(); + void OnWriteComplete(); + + void MaybePostReadCompleteTask(); + void MaybePostWriteCompleteTask(); + + StaticSocketDataHelper helper_; + int sequence_number_; + IoState read_state_; + IoState write_state_; + + base::WeakPtrFactory<SequencedSocketData> weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(SequencedSocketData); +}; + class DeterministicMockTCPClientSocket; // This class gives the user full control over the network activity, @@ -778,6 +846,7 @@ // AsyncSocket: void OnReadComplete(const MockRead& data) override; + void OnWriteComplete(int rv) override; void OnConnectComplete(const MockConnect& data) override; private: @@ -795,10 +864,11 @@ // TCPClientSocket. bool peer_closed_connection_; - // While an asynchronous IO is pending, we save our user-buffer state. - scoped_refptr<IOBuffer> pending_buf_; - int pending_buf_len_; - CompletionCallback pending_callback_; + // While an asynchronous read is pending, we save our user-buffer state. + scoped_refptr<IOBuffer> pending_read_buf_; + int pending_read_buf_len_; + CompletionCallback pending_read_callback_; + CompletionCallback pending_write_callback_; bool was_used_to_convey_data_; DISALLOW_COPY_AND_ASSIGN(MockTCPClientSocket); @@ -888,6 +958,7 @@ // AsyncSocket implementation. void OnReadComplete(const MockRead& data) override; + void OnWriteComplete(int rv) override; void OnConnectComplete(const MockConnect& data) override; void set_source_port(uint16 port) { source_port_ = port; } @@ -938,6 +1009,7 @@ // AsyncSocket: void OnReadComplete(const MockRead& data) override; + void OnWriteComplete(int rv) override; void OnConnectComplete(const MockConnect& data) override; private: @@ -981,6 +1053,7 @@ // This MockSocket does not implement the manual async IO feature. void OnReadComplete(const MockRead& data) override; + void OnWriteComplete(int rv) override; void OnConnectComplete(const MockConnect& data) override; bool WasChannelIDSent() const override; @@ -1028,6 +1101,7 @@ // AsyncSocket implementation. void OnReadComplete(const MockRead& data) override; + void OnWriteComplete(int rv) override; void OnConnectComplete(const MockConnect& data) override; void set_source_port(uint16 port) { source_port_ = port;} @@ -1049,9 +1123,10 @@ IPEndPoint peer_addr_; // While an asynchronous IO is pending, we save our user-buffer state. - scoped_refptr<IOBuffer> pending_buf_; - int pending_buf_len_; - CompletionCallback pending_callback_; + scoped_refptr<IOBuffer> pending_read_buf_; + int pending_read_buf_len_; + CompletionCallback pending_read_callback_; + CompletionCallback pending_write_callback_; BoundNetLog net_log_;
diff --git a/net/socket/ssl_client_socket_openssl.cc b/net/socket/ssl_client_socket_openssl.cc index e36534c..7b11ddc 100644 --- a/net/socket/ssl_client_socket_openssl.cc +++ b/net/socket/ssl_client_socket_openssl.cc
@@ -1196,6 +1196,8 @@ << GetLastError(); } #else + // TODO(davidben): Support OCSP stapling when NSS is the system + // certificate verifier. https://crbug.com/479034. NOTREACHED(); #endif }
diff --git a/net/socket/ssl_client_socket_unittest.cc b/net/socket/ssl_client_socket_unittest.cc index a41903e..9267abd5 100644 --- a/net/socket/ssl_client_socket_unittest.cc +++ b/net/socket/ssl_client_socket_unittest.cc
@@ -2106,7 +2106,7 @@ TestCompletionCallback callback; TestNetLog log; - log.SetLogLevel(NetLog::LOG_ALL); + log.SetCaptureMode(NetLogCaptureMode::IncludeSocketBytes()); scoped_ptr<StreamSocket> transport( new TCPClientSocket(addr, &log, NetLog::Source())); int rv = transport->Connect(callback.callback());
diff --git a/net/socket/tcp_socket_win.cc b/net/socket/tcp_socket_win.cc index 2620eba..a7cc7d8 100644 --- a/net/socket/tcp_socket_win.cc +++ b/net/socket/tcp_socket_win.cc
@@ -801,30 +801,25 @@ result = connect(socket_, storage.addr, storage.addr_len); } - if (!result) { - // Connected without waiting! - // - // The MSDN page for connect says: - // With a nonblocking socket, the connection attempt cannot be completed - // immediately. In this case, connect will return SOCKET_ERROR, and - // WSAGetLastError will return WSAEWOULDBLOCK. - // which implies that for a nonblocking socket, connect never returns 0. - // It's not documented whether the event object will be signaled or not - // if connect does return 0. So the code below is essentially dead code - // and we don't know if it's correct. - NOTREACHED(); + // The MSDN page for connect says: + // With a nonblocking socket, the connection attempt cannot be completed + // immediately. In this case, connect will return SOCKET_ERROR, and + // WSAGetLastError will return WSAEWOULDBLOCK. + // which implies that for a nonblocking socket, connect never returns 0. + // It's not clear what the correct thing to do is if connect() + // returns 0. It's not clear whether this ever happens in practice. + // This CHECK() is here to verify that it doesn't. + // TODO(ricea): If the CHECK() fires in canary or dev, revert this + // CL. If it doesn't, reconsider what we want to do here long-term. + CHECK(result); - if (ResetEventIfSignaled(core_->read_overlapped_.hEvent)) - return OK; - } else { - int os_error = WSAGetLastError(); - if (os_error != WSAEWOULDBLOCK) { - LOG(ERROR) << "connect failed: " << os_error; - connect_os_error_ = os_error; - int rv = MapConnectError(os_error); - CHECK_NE(ERR_IO_PENDING, rv); - return rv; - } + int os_error = WSAGetLastError(); + if (os_error != WSAEWOULDBLOCK) { + LOG(ERROR) << "connect failed: " << os_error; + connect_os_error_ = os_error; + int rv = MapConnectError(os_error); + CHECK_NE(ERR_IO_PENDING, rv); + return rv; } // TODO(ricea): Remove ScopedTracker below once crbug.com/436634 is fixed.
diff --git a/net/socket/transport_client_socket_pool.cc b/net/socket/transport_client_socket_pool.cc index eac2868..5263e70 100644 --- a/net/socket/transport_client_socket_pool.cc +++ b/net/socket/transport_client_socket_pool.cc
@@ -481,7 +481,7 @@ void TransportClientSocketPool::NetLogTcpClientSocketPoolRequestedSocket( const BoundNetLog& net_log, const scoped_refptr<TransportSocketParams>* casted_params) { - if (net_log.IsLogging()) { + if (net_log.GetCaptureMode().enabled()) { // TODO(eroman): Split out the host and port parameters. net_log.AddEvent( NetLog::TYPE_TCP_CLIENT_SOCKET_POOL_REQUESTED_SOCKET, @@ -498,7 +498,7 @@ const scoped_refptr<TransportSocketParams>* casted_params = static_cast<const scoped_refptr<TransportSocketParams>*>(params); - if (net_log.IsLogging()) { + if (net_log.GetCaptureMode().enabled()) { // TODO(eroman): Split out the host and port parameters. net_log.AddEvent( NetLog::TYPE_TCP_CLIENT_SOCKET_POOL_REQUESTED_SOCKETS,
diff --git a/net/spdy/spdy_header_block.cc b/net/spdy/spdy_header_block.cc index bbe9c716..92c6234b 100644 --- a/net/spdy/spdy_header_block.cc +++ b/net/spdy/spdy_header_block.cc
@@ -9,17 +9,15 @@ namespace net { -base::Value* SpdyHeaderBlockNetLogCallback( - const SpdyHeaderBlock* headers, - NetLog::LogLevel log_level) { +base::Value* SpdyHeaderBlockNetLogCallback(const SpdyHeaderBlock* headers, + NetLogCaptureMode capture_mode) { base::DictionaryValue* dict = new base::DictionaryValue(); base::DictionaryValue* headers_dict = new base::DictionaryValue(); for (SpdyHeaderBlock::const_iterator it = headers->begin(); it != headers->end(); ++it) { headers_dict->SetWithoutPathExpansion( - it->first, - new base::StringValue( - ElideHeaderValueForNetLog(log_level, it->first, it->second))); + it->first, new base::StringValue(ElideHeaderValueForNetLog( + capture_mode, it->first, it->second))); } dict->Set("headers", headers_dict); return dict;
diff --git a/net/spdy/spdy_header_block.h b/net/spdy/spdy_header_block.h index 7d2a075..b496642 100644 --- a/net/spdy/spdy_header_block.h +++ b/net/spdy/spdy_header_block.h
@@ -21,7 +21,7 @@ // ownership of returned value. NET_EXPORT base::Value* SpdyHeaderBlockNetLogCallback( const SpdyHeaderBlock* headers, - NetLog::LogLevel log_level); + NetLogCaptureMode capture_mode); // Converts NetLog event parameters into a SPDY header block and writes them // to |headers|. |event_param| must have been created by
diff --git a/net/spdy/spdy_header_block_unittest.cc b/net/spdy/spdy_header_block_unittest.cc index b69f37c..99e9ac21 100644 --- a/net/spdy/spdy_header_block_unittest.cc +++ b/net/spdy/spdy_header_block_unittest.cc
@@ -18,8 +18,8 @@ headers["A"] = "a"; headers["B"] = "b"; - scoped_ptr<base::Value> event_param( - SpdyHeaderBlockNetLogCallback(&headers, NetLog::LOG_ALL_BUT_BYTES)); + scoped_ptr<base::Value> event_param(SpdyHeaderBlockNetLogCallback( + &headers, NetLogCaptureMode::IncludeCookiesAndCredentials())); SpdyHeaderBlock headers2; ASSERT_TRUE(SpdyHeaderBlockFromNetLogParam(event_param.get(), &headers2));
diff --git a/net/spdy/spdy_http_stream_unittest.cc b/net/spdy/spdy_http_stream_unittest.cc index 55f5b36..6fecf457 100644 --- a/net/spdy/spdy_http_stream_unittest.cc +++ b/net/spdy/spdy_http_stream_unittest.cc
@@ -126,7 +126,7 @@ MockRead(ASYNC, 0, 0) // EOF }; - HostPortPair host_port_pair("www.google.com", 80); + HostPortPair host_port_pair("www.example.org", 80); SpdySessionKey key(host_port_pair, ProxyServer::Direct(), PRIVACY_MODE_DISABLED); InitSession(reads, arraysize(reads), NULL, 0, key); @@ -151,14 +151,14 @@ CreateMockRead(*resp, 1), MockRead(SYNCHRONOUS, 0, 2) // EOF }; - HostPortPair host_port_pair("www.google.com", 80); + HostPortPair host_port_pair("www.example.org", 80); SpdySessionKey key(host_port_pair, ProxyServer::Direct(), PRIVACY_MODE_DISABLED); InitSession(reads, arraysize(reads), writes, arraysize(writes), key); HttpRequestInfo request; request.method = "GET"; - request.url = GURL("http://www.google.com/"); + request.url = GURL("http://www.example.org/"); TestCompletionCallback callback; HttpResponseInfo response; HttpRequestHeaders headers; @@ -224,14 +224,14 @@ MockRead(ASYNC, 0, 6) // EOF }; - HostPortPair host_port_pair("www.google.com", 80); + HostPortPair host_port_pair("www.example.org", 80); SpdySessionKey key(host_port_pair, ProxyServer::Direct(), PRIVACY_MODE_DISABLED); InitSession(reads, arraysize(reads), writes, arraysize(writes), key); HttpRequestInfo request1; request1.method = "GET"; - request1.url = GURL("http://www.google.com/"); + request1.url = GURL("http://www.example.org/"); TestCompletionCallback callback1; HttpResponseInfo response1; HttpRequestHeaders headers1; @@ -239,7 +239,7 @@ HttpRequestInfo request2; request2.method = "GET"; - request2.url = GURL("http://www.google.com/"); + request2.url = GURL("http://www.example.org/"); TestCompletionCallback callback2; HttpResponseInfo response2; HttpRequestHeaders headers2; @@ -311,7 +311,7 @@ reads.push_back(CreateMockRead(*body, seq++)); reads.push_back(MockRead(SYNCHRONOUS, 0, seq++)); // EOF - HostPortPair host_port_pair("www.google.com", 80); + HostPortPair host_port_pair("www.example.org", 80); SpdySessionKey key(host_port_pair, ProxyServer::Direct(), PRIVACY_MODE_DISABLED); InitSession(vector_as_array(&reads), reads.size(), vector_as_array(&writes), @@ -326,7 +326,7 @@ HttpRequestInfo request; request.method = "POST"; - request.url = GURL("http://www.google.com/"); + request.url = GURL("http://www.example.org/"); request.upload_data_stream = &upload_stream; ASSERT_EQ(OK, upload_stream.Init(TestCompletionCallback().callback())); @@ -381,7 +381,7 @@ MockRead(ASYNC, 0, 8) // EOF }; - HostPortPair host_port_pair("www.google.com", 80); + HostPortPair host_port_pair("www.example.org", 80); SpdySessionKey key(host_port_pair, ProxyServer::Direct(), PRIVACY_MODE_DISABLED); InitSession(reads, arraysize(reads), writes, arraysize(writes), key); @@ -390,7 +390,7 @@ HttpRequestInfo request; request.method = "POST"; - request.url = GURL("http://www.google.com/"); + request.url = GURL("http://www.example.org/"); request.upload_data_stream = &upload_stream; ASSERT_EQ(OK, upload_stream.Init(TestCompletionCallback().callback())); @@ -478,7 +478,7 @@ MockRead(ASYNC, 0, 6) // EOF }; - HostPortPair host_port_pair("www.google.com", 80); + HostPortPair host_port_pair("www.example.org", 80); SpdySessionKey key(host_port_pair, ProxyServer::Direct(), PRIVACY_MODE_DISABLED); InitSession(reads, arraysize(reads), writes, arraysize(writes), key); @@ -487,7 +487,7 @@ HttpRequestInfo request; request.method = "POST"; - request.url = GURL("http://www.google.com/"); + request.url = GURL("http://www.example.org/"); request.upload_data_stream = &upload_stream; ASSERT_EQ(OK, upload_stream.Init(TestCompletionCallback().callback())); @@ -561,7 +561,7 @@ MockRead(ASYNC, 0, 4) // EOF }; - HostPortPair host_port_pair("www.google.com", 80); + HostPortPair host_port_pair("www.example.org", 80); SpdySessionKey key(host_port_pair, ProxyServer::Direct(), PRIVACY_MODE_DISABLED); InitSession(reads, arraysize(reads), writes, arraysize(writes), key); @@ -570,7 +570,7 @@ HttpRequestInfo request; request.method = "POST"; - request.url = GURL("http://www.google.com/"); + request.url = GURL("http://www.example.org/"); request.upload_data_stream = &upload_stream; ASSERT_EQ(OK, upload_stream.Init(TestCompletionCallback().callback())); @@ -616,8 +616,8 @@ // Test case for bug: http://code.google.com/p/chromium/issues/detail?id=50058 TEST_P(SpdyHttpStreamTest, SpdyURLTest) { - const char * const full_url = "http://www.google.com/foo?query=what#anchor"; - const char * const base_url = "http://www.google.com/foo?query=what"; + const char* const full_url = "http://www.example.org/foo?query=what#anchor"; + const char* const base_url = "http://www.example.org/foo?query=what"; scoped_ptr<SpdyFrame> req( spdy_util_.ConstructSpdyGet(base_url, false, 1, LOWEST)); MockWrite writes[] = { @@ -628,7 +628,7 @@ CreateMockRead(*resp, 1), MockRead(SYNCHRONOUS, 0, 2) // EOF }; - HostPortPair host_port_pair("www.google.com", 80); + HostPortPair host_port_pair("www.example.org", 80); SpdySessionKey key(host_port_pair, ProxyServer::Direct(), PRIVACY_MODE_DISABLED); InitSession(reads, arraysize(reads), writes, arraysize(writes), key); @@ -681,7 +681,7 @@ MockRead(ASYNC, 0, 5) // EOF }; - HostPortPair host_port_pair("www.google.com", 80); + HostPortPair host_port_pair("www.example.org", 80); SpdySessionKey key(host_port_pair, ProxyServer::Direct(), PRIVACY_MODE_DISABLED); @@ -691,7 +691,7 @@ HttpRequestInfo request; request.method = "POST"; - request.url = GURL("http://www.google.com/"); + request.url = GURL("http://www.example.org/"); request.upload_data_stream = &upload_stream; ASSERT_EQ(OK, upload_stream.Init(TestCompletionCallback().callback()));
diff --git a/net/spdy/spdy_network_transaction_unittest.cc b/net/spdy/spdy_network_transaction_unittest.cc index e8d8e0b5..aad489b 100644 --- a/net/spdy/spdy_network_transaction_unittest.cc +++ b/net/spdy/spdy_network_transaction_unittest.cc
@@ -18,6 +18,7 @@ #include "net/base/chunked_upload_data_stream.h" #include "net/base/elements_upload_data_stream.h" #include "net/base/request_priority.h" +#include "net/base/test_data_directory.h" #include "net/base/upload_bytes_element_reader.h" #include "net/base/upload_file_element_reader.h" #include "net/http/http_network_session_peer.h" @@ -35,6 +36,7 @@ #include "net/spdy/spdy_test_util_common.h" #include "net/spdy/spdy_test_utils.h" #include "net/ssl/ssl_connection_status_flags.h" +#include "net/test/cert_test_util.h" #include "net/url_request/url_request_test_util.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/platform_test.h" @@ -92,9 +94,9 @@ session_deps->next_protos = SpdyNextProtos(); if (test_params.ssl_type == HTTP_SPDY_VIA_ALT_SVC) { session_deps->http_server_properties.SetAlternativeService( - HostPortPair("www.google.com", 80), + HostPortPair("www.example.org", 80), AlternativeService(AlternateProtocolFromNextProto(test_params.protocol), - "www.google.com", 443), + "www.example.org", 443), 1); } } @@ -133,9 +135,9 @@ } void SetUp() override { - google_get_request_initialized_ = false; - google_post_request_initialized_ = false; - google_chunked_post_request_initialized_ = false; + get_request_initialized_ = false; + post_request_initialized_ = false; + chunked_post_request_initialized_ = false; ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); } @@ -313,6 +315,8 @@ void AddData(StaticSocketDataProvider* data) { scoped_ptr<SSLSocketDataProvider> ssl_provider( new SSLSocketDataProvider(ASYNC, OK)); + ssl_provider->cert = + ImportCertFromFile(GetTestCertsDirectory(), "spdy_pooling.pem"); AddDataWithSSLSocketDataProvider(data, ssl_provider.Pass()); } @@ -348,6 +352,8 @@ SSLSocketDataProvider* ssl_provider = new SSLSocketDataProvider(ASYNC, OK); ssl_provider->SetNextProto(test_params_.protocol); + ssl_provider->cert = + ImportCertFromFile(GetTestCertsDirectory(), "spdy_pooling.pem"); ssl_vector_.push_back(ssl_provider); session_deps_->deterministic_socket_factory->AddSSLSocketDataProvider( ssl_provider); @@ -413,51 +419,51 @@ void ConnectStatusHelper(const MockRead& status); const HttpRequestInfo& CreateGetPushRequest() { - google_get_push_request_.method = "GET"; - google_get_push_request_.url = GURL(GetDefaultUrlWithPath("/foo.dat")); - google_get_push_request_.load_flags = 0; - return google_get_push_request_; + get_push_request_.method = "GET"; + get_push_request_.url = GURL(GetDefaultUrlWithPath("/foo.dat")); + get_push_request_.load_flags = 0; + return get_push_request_; } const HttpRequestInfo& CreateGetRequest() { - if (!google_get_request_initialized_) { - google_get_request_.method = "GET"; - google_get_request_.url = GURL(GetDefaultUrl()); - google_get_request_.load_flags = 0; - google_get_request_initialized_ = true; + if (!get_request_initialized_) { + get_request_.method = "GET"; + get_request_.url = GURL(GetDefaultUrl()); + get_request_.load_flags = 0; + get_request_initialized_ = true; } - return google_get_request_; + return get_request_; } const HttpRequestInfo& CreateGetRequestWithUserAgent() { - if (!google_get_request_initialized_) { - google_get_request_.method = "GET"; - google_get_request_.url = GURL(GetDefaultUrl()); - google_get_request_.load_flags = 0; - google_get_request_.extra_headers.SetHeader("User-Agent", "Chrome"); - google_get_request_initialized_ = true; + if (!get_request_initialized_) { + get_request_.method = "GET"; + get_request_.url = GURL(GetDefaultUrl()); + get_request_.load_flags = 0; + get_request_.extra_headers.SetHeader("User-Agent", "Chrome"); + get_request_initialized_ = true; } - return google_get_request_; + return get_request_; } const HttpRequestInfo& CreatePostRequest() { - if (!google_post_request_initialized_) { + if (!post_request_initialized_) { ScopedVector<UploadElementReader> element_readers; element_readers.push_back( new UploadBytesElementReader(kUploadData, kUploadDataSize)); upload_data_stream_.reset( new ElementsUploadDataStream(element_readers.Pass(), 0)); - google_post_request_.method = "POST"; - google_post_request_.url = GURL(GetDefaultUrl()); - google_post_request_.upload_data_stream = upload_data_stream_.get(); - google_post_request_initialized_ = true; + post_request_.method = "POST"; + post_request_.url = GURL(GetDefaultUrl()); + post_request_.upload_data_stream = upload_data_stream_.get(); + post_request_initialized_ = true; } - return google_post_request_; + return post_request_; } const HttpRequestInfo& CreateFilePostRequest() { - if (!google_post_request_initialized_) { + if (!post_request_initialized_) { base::FilePath file_path; CHECK(base::CreateTemporaryFileInDir(temp_dir_.path(), &file_path)); CHECK_EQ(static_cast<int>(kUploadDataSize), @@ -473,17 +479,17 @@ upload_data_stream_.reset( new ElementsUploadDataStream(element_readers.Pass(), 0)); - google_post_request_.method = "POST"; - google_post_request_.url = GURL(GetDefaultUrl()); - google_post_request_.upload_data_stream = upload_data_stream_.get(); - google_post_request_initialized_ = true; + post_request_.method = "POST"; + post_request_.url = GURL(GetDefaultUrl()); + post_request_.upload_data_stream = upload_data_stream_.get(); + post_request_initialized_ = true; } - return google_post_request_; + return post_request_; } const HttpRequestInfo& CreateUnreadableFilePostRequest() { - if (google_post_request_initialized_) - return google_post_request_; + if (post_request_initialized_) + return post_request_; base::FilePath file_path; CHECK(base::CreateTemporaryFileInDir(temp_dir_.path(), &file_path)); @@ -501,15 +507,15 @@ upload_data_stream_.reset( new ElementsUploadDataStream(element_readers.Pass(), 0)); - google_post_request_.method = "POST"; - google_post_request_.url = GURL(GetDefaultUrl()); - google_post_request_.upload_data_stream = upload_data_stream_.get(); - google_post_request_initialized_ = true; - return google_post_request_; + post_request_.method = "POST"; + post_request_.url = GURL(GetDefaultUrl()); + post_request_.upload_data_stream = upload_data_stream_.get(); + post_request_initialized_ = true; + return post_request_; } const HttpRequestInfo& CreateComplexPostRequest() { - if (!google_post_request_initialized_) { + if (!post_request_initialized_) { const int kFileRangeOffset = 1; const int kFileRangeLength = 3; CHECK_LT(kFileRangeOffset + kFileRangeLength, kUploadDataSize); @@ -534,24 +540,24 @@ upload_data_stream_.reset( new ElementsUploadDataStream(element_readers.Pass(), 0)); - google_post_request_.method = "POST"; - google_post_request_.url = GURL(GetDefaultUrl()); - google_post_request_.upload_data_stream = upload_data_stream_.get(); - google_post_request_initialized_ = true; + post_request_.method = "POST"; + post_request_.url = GURL(GetDefaultUrl()); + post_request_.upload_data_stream = upload_data_stream_.get(); + post_request_initialized_ = true; } - return google_post_request_; + return post_request_; } const HttpRequestInfo& CreateChunkedPostRequest() { - if (!google_chunked_post_request_initialized_) { + if (!chunked_post_request_initialized_) { upload_chunked_data_stream_.reset(new ChunkedUploadDataStream(0)); - google_chunked_post_request_.method = "POST"; - google_chunked_post_request_.url = GURL(GetDefaultUrl()); - google_chunked_post_request_.upload_data_stream = + chunked_post_request_.method = "POST"; + chunked_post_request_.url = GURL(GetDefaultUrl()); + chunked_post_request_.upload_data_stream = upload_chunked_data_stream_.get(); - google_chunked_post_request_initialized_ = true; + chunked_post_request_initialized_ = true; } - return google_chunked_post_request_; + return chunked_post_request_; } // Read the result of a particular transaction, knowing that we've got @@ -684,9 +690,9 @@ const char* GetDefaultUrl() { switch (GetParam().ssl_type) { case HTTP_SPDY_VIA_ALT_SVC: - return "http://www.google.com"; + return "http://www.example.org"; case HTTPS_SPDY_VIA_NPN: - return "https://www.google.com"; + return "https://www.example.org"; default: NOTREACHED(); return ""; @@ -702,13 +708,13 @@ private: scoped_ptr<ChunkedUploadDataStream> upload_chunked_data_stream_; scoped_ptr<UploadDataStream> upload_data_stream_; - bool google_get_request_initialized_; - bool google_post_request_initialized_; - bool google_chunked_post_request_initialized_; - HttpRequestInfo google_get_request_; - HttpRequestInfo google_post_request_; - HttpRequestInfo google_chunked_post_request_; - HttpRequestInfo google_get_push_request_; + bool get_request_initialized_; + bool post_request_initialized_; + bool chunked_post_request_initialized_; + HttpRequestInfo get_request_; + HttpRequestInfo post_request_; + HttpRequestInfo chunked_post_request_; + HttpRequestInfo get_push_request_; base::ScopedTempDir temp_dir_; }; @@ -2414,7 +2420,7 @@ helper.VerifyDataConsumed(); } -// Send a spdy request to www.google.com that gets redirected to www.foo.com. +// Send a spdy request to www.example.org that gets redirected to www.foo.com. TEST_P(SpdyNetworkTransactionTest, DISABLED_RedirectGetRequest) { scoped_ptr<SpdyHeaderBlock> headers( spdy_util_.ConstructGetHeaderBlock(GetDefaultUrl())); @@ -2425,7 +2431,7 @@ (*headers2)["user-agent"] = ""; (*headers2)["accept-encoding"] = "gzip, deflate"; - // Setup writes/reads to www.google.com + // Setup writes/reads to www.example.org scoped_ptr<SpdyFrame> req( spdy_util_.ConstructSpdySyn(1, *headers, LOWEST, false, true)); scoped_ptr<SpdyFrame> req2( @@ -2486,7 +2492,7 @@ EXPECT_TRUE(data2.at_write_eof()); } -// Send a spdy request to www.google.com. Get a pushed stream that redirects to +// Send a spdy request to www.example.org. Get a pushed stream that redirects to // www.foo.com. TEST_P(SpdyNetworkTransactionTest, DISABLED_RedirectServerPush) { scoped_ptr<SpdyHeaderBlock> headers( @@ -2494,7 +2500,7 @@ (*headers)["user-agent"] = ""; (*headers)["accept-encoding"] = "gzip, deflate"; - // Setup writes/reads to www.google.com + // Setup writes/reads to www.example.org scoped_ptr<SpdyFrame> req( spdy_util_.ConstructSpdySyn(1, *headers, LOWEST, false, true)); scoped_ptr<SpdyFrame> resp(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1)); @@ -3705,7 +3711,8 @@ ASSERT_TRUE(entries[pos].params->GetList("headers", &header_list)); std::vector<std::string> expected; - expected.push_back(std::string(spdy_util_.GetHostKey()) + ": www.google.com"); + expected.push_back(std::string(spdy_util_.GetHostKey()) + + ": www.example.org"); expected.push_back(std::string(spdy_util_.GetPathKey()) + ": /"); expected.push_back(std::string(spdy_util_.GetSchemeKey()) + ": " + spdy_util_.default_url().scheme()); @@ -4189,7 +4196,7 @@ helper.RunPreTestSetup(); // Verify that no settings exist initially. - HostPortPair host_port_pair("www.google.com", helper.port()); + HostPortPair host_port_pair("www.example.org", helper.port()); SpdySessionPool* spdy_session_pool = helper.session()->spdy_session_pool(); EXPECT_TRUE(spdy_session_pool->http_server_properties()->GetSpdySettings( host_port_pair).empty()); @@ -4302,7 +4309,7 @@ pool_peer.SetEnableSendingInitialData(true); // Verify that no settings exist initially. - HostPortPair host_port_pair("www.google.com", helper.port()); + HostPortPair host_port_pair("www.example.org", helper.port()); EXPECT_TRUE(spdy_session_pool->http_server_properties()->GetSpdySettings( host_port_pair).empty()); @@ -4498,7 +4505,7 @@ HttpRequestInfo request; request.method = "GET"; - request.url = GURL(GetDefaultUrl()); + request.url = GURL("https://www.example.org/"); scoped_ptr<SpdySessionDependencies> session_deps( CreateSpdySessionDependencies(GetParam())); // Do not force SPDY so that second socket can negotiate HTTP/1.1. @@ -4532,7 +4539,7 @@ // Second socket: falling back to HTTP/1.1. MockWrite writes1[] = {MockWrite( "GET / HTTP/1.1\r\n" - "Host: www.google.com\r\n" + "Host: www.example.org\r\n" "Connection: keep-alive\r\n\r\n")}; MockRead reads1[] = {MockRead( "HTTP/1.1 200 OK\r\n" @@ -4589,7 +4596,7 @@ HttpRequestInfo request; request.method = "GET"; - request.url = GURL("https://www.google.com/"); + request.url = GURL("https://www.example.org/"); scoped_ptr<SpdySessionDependencies> session_deps( CreateSpdySessionDependencies( GetParam(), @@ -4601,7 +4608,7 @@ // First socket: HTTP/2 CONNECT rejected with HTTP_1_1_REQUIRED. scoped_ptr<SpdyFrame> req(spdy_util_.ConstructSpdyConnect( - nullptr, 0, 1, LOWEST, HostPortPair("www.google.com", 443))); + nullptr, 0, 1, LOWEST, HostPortPair("www.example.org", 443))); MockWrite writes0[] = {CreateMockWrite(*req)}; scoped_ptr<SpdyFrame> go_away(spdy_util_.ConstructSpdyGoAway( 0, GOAWAY_HTTP_1_1_REQUIRED, "Try again using HTTP/1.1 please.")); @@ -4623,12 +4630,12 @@ // Second socket: retry using HTTP/1.1. MockWrite writes1[] = { MockWrite(ASYNC, 1, - "CONNECT www.google.com:443 HTTP/1.1\r\n" - "Host: www.google.com\r\n" + "CONNECT www.example.org:443 HTTP/1.1\r\n" + "Host: www.example.org\r\n" "Proxy-Connection: keep-alive\r\n\r\n"), MockWrite(ASYNC, 3, "GET / HTTP/1.1\r\n" - "Host: www.google.com\r\n" + "Host: www.example.org\r\n" "Connection: keep-alive\r\n\r\n"), }; @@ -4695,8 +4702,8 @@ HttpNetworkTransaction* trans = helper.trans(); const char kConnect443[] = { - "CONNECT www.google.com:443 HTTP/1.1\r\n" - "Host: www.google.com\r\n" + "CONNECT www.example.org:443 HTTP/1.1\r\n" + "Host: www.example.org\r\n" "Proxy-Connection: keep-alive\r\n\r\n"}; const char kHTTP200[] = {"HTTP/1.1 200 OK\r\n\r\n"}; scoped_ptr<SpdyFrame> req( @@ -4738,9 +4745,9 @@ helper.VerifyDataConsumed(); } -// Test to make sure we can correctly connect through a proxy to www.google.com, -// if there already exists a direct spdy connection to www.google.com. See -// http://crbug.com/49874 +// Test to make sure we can correctly connect through a proxy to +// www.example.org, if there already exists a direct spdy connection to +// www.example.org. See https://crbug.com/49874. TEST_P(SpdyNetworkTransactionTest, DirectConnectProxyReconnect) { // When setting up the first transaction, we store the SpdySessionPool so that // we can use the same pool in the second transaction. @@ -4799,7 +4806,7 @@ EXPECT_EQ("hello!", out.response_data); // Check that the SpdySession is still in the SpdySessionPool. - HostPortPair host_port_pair("www.google.com", helper.port()); + HostPortPair host_port_pair("www.example.org", helper.port()); SpdySessionKey session_pool_key_direct( host_port_pair, ProxyServer::Direct(), PRIVACY_MODE_DISABLED); EXPECT_TRUE(HasSpdySession(spdy_session_pool, session_pool_key_direct)); @@ -4810,9 +4817,10 @@ EXPECT_FALSE(HasSpdySession(spdy_session_pool, session_pool_key_proxy)); // Set up data for the proxy connection. - const char kConnect443[] = {"CONNECT www.google.com:443 HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Proxy-Connection: keep-alive\r\n\r\n"}; + const char kConnect443[] = { + "CONNECT www.example.org:443 HTTP/1.1\r\n" + "Host: www.example.org\r\n" + "Proxy-Connection: keep-alive\r\n\r\n"}; const char kHTTP200[] = {"HTTP/1.1 200 OK\r\n\r\n"}; scoped_ptr<SpdyFrame> req2(spdy_util_.ConstructSpdyGet( GetDefaultUrlWithPath("/foo.dat").c_str(), false, 1, LOWEST)); @@ -4833,7 +4841,7 @@ scoped_ptr<OrderedSocketData> data_proxy(new OrderedSocketData( reads2, arraysize(reads2), writes2, arraysize(writes2))); - // Create another request to www.google.com, but this time through a proxy. + // Create another request to www.example.org, but this time through a proxy. HttpRequestInfo request_proxy; request_proxy.method = "GET"; request_proxy.url = GURL(GetDefaultUrlWithPath("/foo.dat")); @@ -5616,19 +5624,19 @@ // A list of the URL to fetch, followed by the URL being pushed. static const char* const kTestCases[] = { - "https://www.google.com/foo.html", - "https://www.google.com:81/foo.js", // Bad port + "https://www.example.org/foo.html", + "https://www.example.org:81/foo.js", // Bad port - "https://www.google.com/foo.html", - "http://www.google.com/foo.js", // Bad protocol + "https://www.example.org/foo.html", + "http://www.example.org/foo.js", // Bad protocol - "https://www.google.com/foo.html", - "ftp://www.google.com/foo.js", // Invalid Protocol + "https://www.example.org/foo.html", + "ftp://www.example.org/foo.js", // Invalid Protocol - "https://www.google.com/foo.html", - "https://blat.www.google.com/foo.js", // Cross subdomain + "https://www.example.org/foo.html", + "https://blat.www.example.org/foo.js", // Cross subdomain - "https://www.google.com/foo.html", + "https://www.example.org/foo.html", "https://www.foo.com/foo.js", // Cross domain }; @@ -6536,7 +6544,7 @@ return; scoped_ptr<SpdyHeaderBlock> push_headers(new SpdyHeaderBlock); - spdy_util_.AddUrlToHeaderBlock("http://www.google.com/a.dat", + spdy_util_.AddUrlToHeaderBlock("http://www.example.org/a.dat", push_headers.get()); scoped_ptr<SpdyFrame> push( spdy_util_.ConstructInitialSpdyPushFrame(push_headers.Pass(), 3, 1)); @@ -6612,7 +6620,7 @@ 1, reads, arraysize(reads), writes, arraysize(writes)); HttpRequestInfo request; request.method = "GET"; - request.url = GURL("https://www.google.com/"); + request.url = GURL("https://www.example.org/"); NormalSpdyTransactionHelper helper( request, DEFAULT_PRIORITY, BoundNetLog(), GetParam(), NULL); helper.RunToCompletionWithSSLData(&data, ssl_provider.Pass()); @@ -6664,7 +6672,7 @@ DelayedSocketData data(1, NULL, 0, writes, arraysize(writes)); HttpRequestInfo request; request.method = "GET"; - request.url = GURL("https://www.google.com/"); + request.url = GURL("https://www.example.org/"); NormalSpdyTransactionHelper helper( request, DEFAULT_PRIORITY, BoundNetLog(), GetParam(), NULL); helper.RunToCompletionWithSSLData(&data, ssl_provider.Pass());
diff --git a/net/spdy/spdy_session.cc b/net/spdy/spdy_session.cc index 2f8af1e..e25a88c 100644 --- a/net/spdy/spdy_session.cc +++ b/net/spdy/spdy_session.cc
@@ -59,13 +59,13 @@ scoped_ptr<base::ListValue> SpdyHeaderBlockToListValue( const SpdyHeaderBlock& headers, - net::NetLog::LogLevel log_level) { + net::NetLogCaptureMode capture_mode) { scoped_ptr<base::ListValue> headers_list(new base::ListValue()); for (SpdyHeaderBlock::const_iterator it = headers.begin(); it != headers.end(); ++it) { headers_list->AppendString( it->first + ": " + - ElideHeaderValueForNetLog(log_level, it->first, it->second)); + ElideHeaderValueForNetLog(capture_mode, it->first, it->second)); } return headers_list.Pass(); } @@ -75,10 +75,10 @@ bool unidirectional, SpdyPriority spdy_priority, SpdyStreamId stream_id, - NetLog::LogLevel log_level) { + NetLogCaptureMode capture_mode) { base::DictionaryValue* dict = new base::DictionaryValue(); dict->Set("headers", - SpdyHeaderBlockToListValue(*headers, log_level).release()); + SpdyHeaderBlockToListValue(*headers, capture_mode).release()); dict->SetBoolean("fin", fin); dict->SetBoolean("unidirectional", unidirectional); dict->SetInteger("priority", static_cast<int>(spdy_priority)); @@ -93,10 +93,10 @@ SpdyPriority spdy_priority, SpdyStreamId stream_id, SpdyStreamId associated_stream, - NetLog::LogLevel log_level) { + NetLogCaptureMode capture_mode) { base::DictionaryValue* dict = new base::DictionaryValue(); dict->Set("headers", - SpdyHeaderBlockToListValue(*headers, log_level).release()); + SpdyHeaderBlockToListValue(*headers, capture_mode).release()); dict->SetBoolean("fin", fin); dict->SetBoolean("unidirectional", unidirectional); dict->SetInteger("priority", static_cast<int>(spdy_priority)); @@ -109,18 +109,19 @@ const SpdyHeaderBlock* headers, bool fin, SpdyStreamId stream_id, - NetLog::LogLevel log_level) { + NetLogCaptureMode capture_mode) { base::DictionaryValue* dict = new base::DictionaryValue(); dict->Set("headers", - SpdyHeaderBlockToListValue(*headers, log_level).release()); + SpdyHeaderBlockToListValue(*headers, capture_mode).release()); dict->SetBoolean("fin", fin); dict->SetInteger("stream_id", stream_id); return dict; } -base::Value* NetLogSpdySessionCloseCallback(int net_error, - const std::string* description, - NetLog::LogLevel /* log_level */) { +base::Value* NetLogSpdySessionCloseCallback( + int net_error, + const std::string* description, + NetLogCaptureMode /* capture_mode */) { base::DictionaryValue* dict = new base::DictionaryValue(); dict->SetInteger("net_error", net_error); dict->SetString("description", *description); @@ -128,16 +129,17 @@ } base::Value* NetLogSpdySessionCallback(const HostPortProxyPair* host_pair, - NetLog::LogLevel /* log_level */) { + NetLogCaptureMode /* capture_mode */) { base::DictionaryValue* dict = new base::DictionaryValue(); dict->SetString("host", host_pair->first.ToString()); dict->SetString("proxy", host_pair->second.ToPacString()); return dict; } -base::Value* NetLogSpdyInitializedCallback(NetLog::Source source, - const NextProto protocol_version, - NetLog::LogLevel /* log_level */) { +base::Value* NetLogSpdyInitializedCallback( + NetLog::Source source, + const NextProto protocol_version, + NetLogCaptureMode /* capture_mode */) { base::DictionaryValue* dict = new base::DictionaryValue(); if (source.IsValid()) { source.AddToEventParameters(dict); @@ -149,7 +151,7 @@ base::Value* NetLogSpdySettingsCallback(const HostPortPair& host_port_pair, bool clear_persisted, - NetLog::LogLevel /* log_level */) { + NetLogCaptureMode /* capture_mode */) { base::DictionaryValue* dict = new base::DictionaryValue(); dict->SetString("host", host_port_pair.ToString()); dict->SetBoolean("clear_persisted", clear_persisted); @@ -160,7 +162,7 @@ const SpdyMajorVersion protocol_version, SpdySettingsFlags flags, uint32 value, - NetLog::LogLevel /* log_level */) { + NetLogCaptureMode /* capture_mode */) { base::DictionaryValue* dict = new base::DictionaryValue(); dict->SetInteger("id", SpdyConstants::SerializeSettingId(protocol_version, id)); @@ -172,7 +174,7 @@ base::Value* NetLogSpdySendSettingsCallback( const SettingsMap* settings, const SpdyMajorVersion protocol_version, - NetLog::LogLevel /* log_level */) { + NetLogCaptureMode /* capture_mode */) { base::DictionaryValue* dict = new base::DictionaryValue(); base::ListValue* settings_list = new base::ListValue(); for (SettingsMap::const_iterator it = settings->begin(); @@ -193,7 +195,7 @@ base::Value* NetLogSpdyWindowUpdateFrameCallback( SpdyStreamId stream_id, uint32 delta, - NetLog::LogLevel /* log_level */) { + NetLogCaptureMode /* capture_mode */) { base::DictionaryValue* dict = new base::DictionaryValue(); dict->SetInteger("stream_id", static_cast<int>(stream_id)); dict->SetInteger("delta", delta); @@ -203,7 +205,7 @@ base::Value* NetLogSpdySessionWindowUpdateCallback( int32 delta, int32 window_size, - NetLog::LogLevel /* log_level */) { + NetLogCaptureMode /* capture_mode */) { base::DictionaryValue* dict = new base::DictionaryValue(); dict->SetInteger("delta", delta); dict->SetInteger("window_size", window_size); @@ -213,7 +215,7 @@ base::Value* NetLogSpdyDataCallback(SpdyStreamId stream_id, int size, bool fin, - NetLog::LogLevel /* log_level */) { + NetLogCaptureMode /* capture_mode */) { base::DictionaryValue* dict = new base::DictionaryValue(); dict->SetInteger("stream_id", static_cast<int>(stream_id)); dict->SetInteger("size", size); @@ -224,7 +226,7 @@ base::Value* NetLogSpdyRstCallback(SpdyStreamId stream_id, int status, const std::string* description, - NetLog::LogLevel /* log_level */) { + NetLogCaptureMode /* capture_mode */) { base::DictionaryValue* dict = new base::DictionaryValue(); dict->SetInteger("stream_id", static_cast<int>(stream_id)); dict->SetInteger("status", status); @@ -235,7 +237,7 @@ base::Value* NetLogSpdyPingCallback(SpdyPingId unique_id, bool is_ack, const char* type, - NetLog::LogLevel /* log_level */) { + NetLogCaptureMode /* capture_mode */) { base::DictionaryValue* dict = new base::DictionaryValue(); dict->SetInteger("unique_id", static_cast<int>(unique_id)); dict->SetString("type", type); @@ -247,7 +249,7 @@ int active_streams, int unclaimed_streams, SpdyGoAwayStatus status, - NetLog::LogLevel /* log_level */) { + NetLogCaptureMode /* capture_mode */) { base::DictionaryValue* dict = new base::DictionaryValue(); dict->SetInteger("last_accepted_stream_id", static_cast<int>(last_stream_id)); @@ -261,17 +263,19 @@ const SpdyHeaderBlock* headers, SpdyStreamId stream_id, SpdyStreamId promised_stream_id, - NetLog::LogLevel log_level) { + NetLogCaptureMode capture_mode) { base::DictionaryValue* dict = new base::DictionaryValue(); dict->Set("headers", - SpdyHeaderBlockToListValue(*headers, log_level).release()); + SpdyHeaderBlockToListValue(*headers, capture_mode).release()); dict->SetInteger("id", stream_id); dict->SetInteger("promised_stream_id", promised_stream_id); return dict; } base::Value* NetLogSpdyAdoptedPushStreamCallback( - SpdyStreamId stream_id, const GURL* url, NetLog::LogLevel log_level) { + SpdyStreamId stream_id, + const GURL* url, + NetLogCaptureMode capture_mode) { base::DictionaryValue* dict = new base::DictionaryValue(); dict->SetInteger("stream_id", stream_id); dict->SetString("url", url->spec()); @@ -1078,7 +1082,7 @@ streams_initiated_count_++; - if (net_log().IsLogging()) { + if (net_log().GetCaptureMode().enabled()) { const NetLog::EventType type = (GetProtocolVersion() <= SPDY3) ? NetLog::TYPE_HTTP2_SESSION_SYN_STREAM @@ -1188,7 +1192,7 @@ if (effective_len < len) flags = static_cast<SpdyDataFlags>(flags & ~DATA_FLAG_FIN); - if (net_log().IsLogging()) { + if (net_log().GetCaptureMode().enabled()) { net_log().AddEvent(NetLog::TYPE_HTTP2_SESSION_SEND_DATA, base::Bind(&NetLogSpdyDataCallback, stream_id, effective_len, (flags & DATA_FLAG_FIN) != 0)); @@ -2042,7 +2046,7 @@ bool fin) { CHECK(in_io_loop_); DCHECK_LT(len, 1u << 24); - if (net_log().IsLogging()) { + if (net_log().GetCaptureMode().enabled()) { net_log().AddEvent( NetLog::TYPE_HTTP2_SESSION_RECV_DATA, base::Bind(&NetLogSpdyDataCallback, stream_id, len, fin)); @@ -2115,7 +2119,7 @@ if (clear_persisted) http_server_properties_->ClearSpdySettings(host_port_pair()); - if (net_log_.IsLogging()) { + if (net_log_.GetCaptureMode().enabled()) { net_log_.AddEvent(NetLog::TYPE_HTTP2_SESSION_RECV_SETTINGS, base::Bind(&NetLogSpdySettingsCallback, host_port_pair(), clear_persisted)); @@ -2228,7 +2232,7 @@ base::Time response_time = base::Time::Now(); base::TimeTicks recv_first_byte_time = time_func_(); - if (net_log_.IsLogging()) { + if (net_log_.GetCaptureMode().enabled()) { net_log_.AddEvent( NetLog::TYPE_HTTP2_SESSION_PUSHED_SYN_STREAM, base::Bind(&NetLogSpdySynStreamReceivedCallback, &headers, fin, @@ -2300,7 +2304,7 @@ base::Time response_time = base::Time::Now(); base::TimeTicks recv_first_byte_time = time_func_(); - if (net_log().IsLogging()) { + if (net_log().GetCaptureMode().enabled()) { net_log().AddEvent(NetLog::TYPE_HTTP2_SESSION_SYN_REPLY, base::Bind(&NetLogSpdySynReplyOrHeadersReceivedCallback, &headers, fin, stream_id)); @@ -2345,7 +2349,7 @@ const SpdyHeaderBlock& headers) { CHECK(in_io_loop_); - if (net_log().IsLogging()) { + if (net_log().GetCaptureMode().enabled()) { net_log().AddEvent(NetLog::TYPE_HTTP2_SESSION_RECV_HEADERS, base::Bind(&NetLogSpdySynReplyOrHeadersReceivedCallback, &headers, fin, stream_id)); @@ -2728,7 +2732,7 @@ const SpdyHeaderBlock& headers) { CHECK(in_io_loop_); - if (net_log_.IsLogging()) { + if (net_log_.GetCaptureMode().enabled()) { net_log_.AddEvent(NetLog::TYPE_HTTP2_SESSION_RECV_PUSH_PROMISE, base::Bind(&NetLogSpdyPushPromiseReceivedCallback, &headers, stream_id, promised_stream_id)); @@ -2922,7 +2926,7 @@ buffered_spdy_framer_->CreatePingFrame(unique_id, is_ack)); EnqueueSessionWrite(HIGHEST, PING, ping_frame.Pass()); - if (net_log().IsLogging()) { + if (net_log().GetCaptureMode().enabled()) { net_log().AddEvent( NetLog::TYPE_HTTP2_SESSION_PING, base::Bind(&NetLogSpdyPingCallback, unique_id, is_ack, "sent"));
diff --git a/net/spdy/spdy_session_unittest.cc b/net/spdy/spdy_session_unittest.cc index af8ba39..a470606 100644 --- a/net/spdy/spdy_session_unittest.cc +++ b/net/spdy/spdy_session_unittest.cc
@@ -1192,13 +1192,13 @@ spdy_util_.ConstructSpdyRstStream(2, RST_STREAM_REFUSED_STREAM)); scoped_ptr<SpdyFrame> push_a(spdy_util_.ConstructSpdyPush( - NULL, 0, 2, 1, "http://www.google.com/a.dat")); + NULL, 0, 2, 1, "http://www.example.org/a.dat")); scoped_ptr<SpdyFrame> push_a_body( spdy_util_.ConstructSpdyBodyFrame(2, false)); // In ascii "0" < "a". We use it to verify that we properly handle std::map // iterators inside. See http://crbug.com/443490 scoped_ptr<SpdyFrame> push_b(spdy_util_.ConstructSpdyPush( - NULL, 0, 4, 1, "http://www.google.com/0.dat")); + NULL, 0, 4, 1, "http://www.example.org/0.dat")); MockWrite writes[] = {CreateMockWrite(*req, 0), CreateMockWrite(*rst, 4)}; MockRead reads[] = { CreateMockRead(*push_a, 1), CreateMockRead(*push_a_body, 2), @@ -1232,7 +1232,7 @@ EXPECT_EQ(1u, session->num_unclaimed_pushed_streams()); SpdySession::PushedStreamMap::iterator iter = session->unclaimed_pushed_streams_.find( - GURL("http://www.google.com/a.dat")); + GURL("http://www.example.org/a.dat")); EXPECT_TRUE(session->unclaimed_pushed_streams_.end() != iter); if (session->flow_control_state_ == @@ -1252,7 +1252,7 @@ // Verify that the second pushed stream evicted the first pushed stream. EXPECT_EQ(1u, session->num_unclaimed_pushed_streams()); iter = session->unclaimed_pushed_streams_.find( - GURL("http://www.google.com/0.dat")); + GURL("http://www.example.org/0.dat")); EXPECT_TRUE(session->unclaimed_pushed_streams_.end() != iter); if (session->flow_control_state_ == @@ -1787,7 +1787,7 @@ break; case SPDY4: histogram_tester.ExpectBucketCount( - "Net.SpdySynStreamCompressionPercentage", 82, 1); + "Net.SpdySynStreamCompressionPercentage", 81, 1); break; default: NOTREACHED(); @@ -3303,7 +3303,7 @@ TEST_P(SpdySessionTest, SpdySessionKeyPrivacyMode) { CreateDeterministicNetworkSession(); - HostPortPair host_port_pair("www.google.com", 443); + HostPortPair host_port_pair("www.example.org", 443); SpdySessionKey key_privacy_enabled(host_port_pair, ProxyServer::Direct(), PRIVACY_MODE_ENABLED); SpdySessionKey key_privacy_disabled(host_port_pair, ProxyServer::Direct(), @@ -3674,7 +3674,7 @@ if (GetParam() < kProtoSPDY31) return; - const char kStreamUrl[] = "http://www.google.com/"; + const char kStreamUrl[] = "http://www.example.org/"; const int32 msg_data_size = 100; const std::string msg_data(msg_data_size, 'a'); @@ -3762,7 +3762,7 @@ if (GetParam() < kProtoSPDY31) return; - const char kStreamUrl[] = "http://www.google.com/"; + const char kStreamUrl[] = "http://www.example.org/"; const int32 msg_data_size = 100; const std::string msg_data(msg_data_size, 'a'); @@ -3841,7 +3841,7 @@ if (GetParam() < kProtoSPDY31) return; - const char kStreamUrl[] = "http://www.google.com/"; + const char kStreamUrl[] = "http://www.example.org/"; const int32 msg_data_size = 100; const std::string msg_data(msg_data_size, 'a'); @@ -3969,7 +3969,7 @@ const base::Callback<void(SpdySession*, SpdyStream*)>& stall_function, const base::Callback<void(SpdySession*, SpdyStream*, int32)>& unstall_function) { - const char kStreamUrl[] = "http://www.google.com/"; + const char kStreamUrl[] = "http://www.example.org/"; GURL url(kStreamUrl); session_deps_.host_resolver->set_synchronous_mode(true); @@ -4122,7 +4122,7 @@ if (GetParam() < kProtoSPDY31) return; - const char kStreamUrl[] = "http://www.google.com/"; + const char kStreamUrl[] = "http://www.example.org/"; GURL url(kStreamUrl); session_deps_.host_resolver->set_synchronous_mode(true); @@ -4276,7 +4276,7 @@ if (GetParam() < kProtoSPDY31) return; - const char kStreamUrl[] = "http://www.google.com/"; + const char kStreamUrl[] = "http://www.example.org/"; GURL url(kStreamUrl); session_deps_.host_resolver->set_synchronous_mode(true); @@ -4438,7 +4438,7 @@ if (GetParam() < kProtoSPDY31) return; - const char kStreamUrl[] = "http://www.google.com/"; + const char kStreamUrl[] = "http://www.example.org/"; GURL url(kStreamUrl); session_deps_.host_resolver->set_synchronous_mode(true); @@ -4604,7 +4604,7 @@ } TEST_P(SpdySessionTest, SplitHeaders) { - GURL kStreamUrl("http://www.google.com/foo.dat"); + GURL kStreamUrl("http://www.example.org/foo.dat"); SpdyHeaderBlock headers; spdy_util_.AddUrlToHeaderBlock(kStreamUrl.spec(), &headers); headers["alpha"] = "beta"; @@ -4634,7 +4634,7 @@ scoped_ptr<SpdyFrame> settings_frame( spdy_util_.ConstructSpdySettings(new_settings)); scoped_ptr<SpdyFrame> pushed(spdy_util_.ConstructSpdyPush( - NULL, 0, 2, 1, "http://www.google.com/a.dat")); + NULL, 0, 2, 1, "http://www.example.org/a.dat")); MockRead reads[] = { CreateMockRead(*settings_frame), CreateMockRead(*pushed, 3), MockRead(ASYNC, 0, 4), @@ -4712,9 +4712,9 @@ TEST_P(SpdySessionTest, RejectPushedStreamExceedingConcurrencyLimit) { scoped_ptr<SpdyFrame> push_a(spdy_util_.ConstructSpdyPush( - NULL, 0, 2, 1, "http://www.google.com/a.dat")); + NULL, 0, 2, 1, "http://www.example.org/a.dat")); scoped_ptr<SpdyFrame> push_b(spdy_util_.ConstructSpdyPush( - NULL, 0, 4, 1, "http://www.google.com/b.dat")); + NULL, 0, 4, 1, "http://www.example.org/b.dat")); MockRead reads[] = { CreateMockRead(*push_a, 1), CreateMockRead(*push_b, 2), MockRead(ASYNC, 0, 4), @@ -4791,9 +4791,9 @@ return; scoped_ptr<SpdyFrame> push_a(spdy_util_.ConstructSpdyPush( - NULL, 0, 2, 1, "http://www.google.com/a.dat")); + NULL, 0, 2, 1, "http://www.example.org/a.dat")); scoped_ptr<SpdyHeaderBlock> push_headers(new SpdyHeaderBlock); - spdy_util_.AddUrlToHeaderBlock("http://www.google.com/b.dat", + spdy_util_.AddUrlToHeaderBlock("http://www.example.org/b.dat", push_headers.get()); scoped_ptr<SpdyFrame> push_b( spdy_util_.ConstructInitialSpdyPushFrame(push_headers.Pass(), 4, 1)); @@ -4882,7 +4882,7 @@ if (spdy_util_.spdy_version() < SPDY4) return; - const char kPushedUrl[] = "http://www.google.com/a.dat"; + const char kPushedUrl[] = "http://www.example.org/a.dat"; scoped_ptr<SpdyHeaderBlock> push_headers(new SpdyHeaderBlock); spdy_util_.AddUrlToHeaderBlock(kPushedUrl, push_headers.get()); scoped_ptr<SpdyFrame> push_promise(
diff --git a/net/spdy/spdy_stream.cc b/net/spdy/spdy_stream.cc index 7f29364..f841be1 100644 --- a/net/spdy/spdy_stream.cc +++ b/net/spdy/spdy_stream.cc
@@ -20,10 +20,11 @@ namespace { -base::Value* NetLogSpdyStreamErrorCallback(SpdyStreamId stream_id, - int status, - const std::string* description, - NetLog::LogLevel /* log_level */) { +base::Value* NetLogSpdyStreamErrorCallback( + SpdyStreamId stream_id, + int status, + const std::string* description, + NetLogCaptureMode /* capture_mode */) { base::DictionaryValue* dict = new base::DictionaryValue(); dict->SetInteger("stream_id", static_cast<int>(stream_id)); dict->SetInteger("status", status); @@ -35,7 +36,7 @@ SpdyStreamId stream_id, int32 delta, int32 window_size, - NetLog::LogLevel /* log_level */) { + NetLogCaptureMode /* capture_mode */) { base::DictionaryValue* dict = new base::DictionaryValue(); dict->SetInteger("stream_id", stream_id); dict->SetInteger("delta", delta);
diff --git a/net/spdy/spdy_stream_unittest.cc b/net/spdy/spdy_stream_unittest.cc index fc8d35f2..15f67301 100644 --- a/net/spdy/spdy_stream_unittest.cc +++ b/net/spdy/spdy_stream_unittest.cc
@@ -32,7 +32,7 @@ namespace { -const char kStreamUrl[] = "http://www.google.com/"; +const char kStreamUrl[] = "http://www.example.org/"; const char kPostBody[] = "\0hello!\xff"; const size_t kPostBodyLength = arraysize(kPostBody); const base::StringPiece kPostBodyStringPiece(kPostBody, kPostBodyLength); @@ -51,9 +51,8 @@ offset_(0) {} base::WeakPtr<SpdySession> CreateDefaultSpdySession() { - SpdySessionKey key(HostPortPair("www.google.com", 80), - ProxyServer::Direct(), - PRIVACY_MODE_DISABLED); + SpdySessionKey key(HostPortPair("www.example.org", 80), + ProxyServer::Direct(), PRIVACY_MODE_DISABLED); return CreateInsecureSpdySession(session_, key, BoundNetLog()); }
diff --git a/net/spdy/spdy_test_util_common.h b/net/spdy/spdy_test_util_common.h index d95b69f..781dbb2 100644 --- a/net/spdy/spdy_test_util_common.h +++ b/net/spdy/spdy_test_util_common.h
@@ -44,7 +44,7 @@ // Default upload data used by both, mock objects and framer when creating // data frames. -const char kDefaultURL[] = "http://www.google.com"; +const char kDefaultURL[] = "http://www.example.org"; const char kUploadData[] = "hello!"; const int kUploadDataSize = arraysize(kUploadData)-1;
diff --git a/net/ssl/channel_id_service.cc b/net/ssl/channel_id_service.cc index e52d470c..7fcbc53 100644 --- a/net/ssl/channel_id_service.cc +++ b/net/ssl/channel_id_service.cc
@@ -27,7 +27,7 @@ #include "net/cert/x509_util.h" #include "url/gurl.h" -#if defined(USE_NSS_CERTS) +#if !defined(USE_OPENSSL) #include <private/pprthred.h> // PR_DetachThread #endif @@ -246,7 +246,7 @@ scoped_ptr<ChannelIDStore::ChannelID> cert = GenerateChannelID(server_identifier_, serial_number_, &error); DVLOG(1) << "GenerateCert " << server_identifier_ << " returned " << error; -#if defined(USE_NSS_CERTS) +#if !defined(USE_OPENSSL) // Detach the thread from NSPR. // Calling NSS functions attaches the thread to NSPR, which stores // the NSPR thread ID in thread-specific data.
diff --git a/net/ssl/openssl_platform_key_nss.cc b/net/ssl/openssl_platform_key_nss.cc new file mode 100644 index 0000000..a9471e4 --- /dev/null +++ b/net/ssl/openssl_platform_key_nss.cc
@@ -0,0 +1,17 @@ +// 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/logging.h" +#include "net/ssl/openssl_platform_key.h" + +namespace net { + +crypto::ScopedEVP_PKEY FetchClientCertPrivateKey( + const X509Certificate* certificate) { + // TODO(davidben): Implement client auth for NSS. https://crbug.com/479036 + NOTIMPLEMENTED(); + return nullptr; +} + +} // namespace net
diff --git a/net/ssl/openssl_ssl_util.cc b/net/ssl/openssl_ssl_util.cc index f7acd62..54a3400 100644 --- a/net/ssl/openssl_ssl_util.cc +++ b/net/ssl/openssl_ssl_util.cc
@@ -155,7 +155,7 @@ base::Value* NetLogOpenSSLErrorCallback(int net_error, int ssl_error, const OpenSSLErrorInfo& error_info, - NetLog::LogLevel /* log_level */) { + NetLogCaptureMode /* capture_mode */) { base::DictionaryValue* dict = new base::DictionaryValue(); dict->SetInteger("net_error", net_error); dict->SetInteger("ssl_error", ssl_error);
diff --git a/net/test/cert_test_util_nss.cc b/net/test/cert_test_util_nss.cc index ee929e5a..74884c7dd 100644 --- a/net/test/cert_test_util_nss.cc +++ b/net/test/cert_test_util_nss.cc
@@ -19,6 +19,14 @@ const base::FilePath& dir, const std::string& key_filename, PK11SlotInfo* slot) { +#if defined(USE_OPENSSL) + // TODO(davidben): Port RSAPrivateKey::CreateSensitiveFromPrivateKeyInfo away + // from RSAPrivateKey so it doesn't make assumptions about the internal crypto + // library. Instead, return a ScopedSECKEYPrivateKey or have this function + // just return bool. https://crbug.com/478777 + NOTIMPLEMENTED(); + return nullptr; +#else base::FilePath key_path = dir.AppendASCII(key_filename); std::string key_pkcs8; bool success = base::ReadFileToString(key_path, &key_pkcs8); @@ -38,6 +46,7 @@ LOG_IF(ERROR, !private_key) << "Could not create key from file " << key_path.value(); return private_key.Pass(); +#endif } bool ImportClientCertToSlot(const scoped_refptr<X509Certificate>& cert,
diff --git a/net/test/net_test_suite.cc b/net/test/net_test_suite.cc index ac927e5c..b14e3fe 100644 --- a/net/test/net_test_suite.cc +++ b/net/test/net_test_suite.cc
@@ -15,7 +15,7 @@ #endif class StaticReset : public ::testing::EmptyTestEventListener { - virtual void OnTestStart(const ::testing::TestInfo& test_info) override { + void OnTestStart(const ::testing::TestInfo& test_info) override { net::HttpStreamFactory::ResetStaticSettingsToInit(); } };
diff --git a/net/test/spawned_test_server/base_test_server.cc b/net/test/spawned_test_server/base_test_server.cc index 3eeb685..b8ded1f 100644 --- a/net/test/spawned_test_server/base_test_server.cc +++ b/net/test/spawned_test_server/base_test_server.cc
@@ -310,6 +310,24 @@ return true; } +bool BaseTestServer::LoadTestRootCert() const { + TestRootCerts* root_certs = TestRootCerts::GetInstance(); + if (!root_certs) + return false; + + // Should always use absolute path to load the root certificate. + base::FilePath root_certificate_path = certificates_dir_; + if (!certificates_dir_.IsAbsolute()) { + base::FilePath src_dir; + if (!PathService::Get(base::DIR_SOURCE_ROOT, &src_dir)) + return false; + root_certificate_path = src_dir.Append(certificates_dir_); + } + + return root_certs->AddFromFile( + root_certificate_path.AppendASCII("root_ca_cert.pem")); +} + void BaseTestServer::Init(const std::string& host) { host_port_pair_ = HostPortPair(host, 0); @@ -352,24 +370,6 @@ return true; } -bool BaseTestServer::LoadTestRootCert() const { - TestRootCerts* root_certs = TestRootCerts::GetInstance(); - if (!root_certs) - return false; - - // Should always use absolute path to load the root certificate. - base::FilePath root_certificate_path = certificates_dir_; - if (!certificates_dir_.IsAbsolute()) { - base::FilePath src_dir; - if (!PathService::Get(base::DIR_SOURCE_ROOT, &src_dir)) - return false; - root_certificate_path = src_dir.Append(certificates_dir_); - } - - return root_certs->AddFromFile( - root_certificate_path.AppendASCII("root_ca_cert.pem")); -} - bool BaseTestServer::SetupWhenServerStarted() { DCHECK(host_port_pair_.port());
diff --git a/net/test/spawned_test_server/base_test_server.h b/net/test/spawned_test_server/base_test_server.h index 5fd57f1..d430b0e 100644 --- a/net/test/spawned_test_server/base_test_server.h +++ b/net/test/spawned_test_server/base_test_server.h
@@ -262,6 +262,10 @@ ws_basic_auth_ = ws_basic_auth; } + // Marks the root certificate of an HTTPS test server as trusted for + // the duration of tests. + bool LoadTestRootCert() const WARN_UNUSED_RESULT; + protected: virtual ~BaseTestServer(); Type type() const { return type_; } @@ -300,10 +304,6 @@ private: void Init(const std::string& host); - // Marks the root certificate of an HTTPS test server as trusted for - // the duration of tests. - bool LoadTestRootCert() const WARN_UNUSED_RESULT; - // Document root of the test server. base::FilePath document_root_;
diff --git a/net/tools/gdig/gdig.cc b/net/tools/gdig/gdig.cc index faaffa7..285668b5 100644 --- a/net/tools/gdig/gdig.cc +++ b/net/tools/gdig/gdig.cc
@@ -296,15 +296,17 @@ if (parsed_command_line.HasSwitch("net_log")) { std::string log_param = parsed_command_line.GetSwitchValueASCII("net_log"); - NetLog::LogLevel level = NetLog::LOG_ALL_BUT_BYTES; + NetLogCaptureMode capture_mode = + NetLogCaptureMode::IncludeCookiesAndCredentials(); if (log_param.length() > 0) { - std::map<std::string, NetLog::LogLevel> log_levels; - log_levels["all"] = NetLog::LOG_ALL; - log_levels["no_bytes"] = NetLog::LOG_ALL_BUT_BYTES; + std::map<std::string, NetLogCaptureMode> capture_modes; + capture_modes["all"] = NetLogCaptureMode::IncludeSocketBytes(); + capture_modes["no_bytes"] = + NetLogCaptureMode::IncludeCookiesAndCredentials(); - if (log_levels.find(log_param) != log_levels.end()) { - level = log_levels[log_param]; + if (capture_modes.find(log_param) != capture_modes.end()) { + capture_mode = capture_modes[log_param]; } else { fprintf(stderr, "Invalid net_log parameter\n"); return false; @@ -312,7 +314,7 @@ } log_.reset(new NetLog); log_observer_.reset(new FileNetLogObserver(stderr)); - log_->DeprecatedAddObserver(log_observer_.get(), level); + log_->DeprecatedAddObserver(log_observer_.get(), capture_mode); } print_config_ = parsed_command_line.HasSwitch("print_config");
diff --git a/net/tools/get_server_time/get_server_time.cc b/net/tools/get_server_time/get_server_time.cc index e8d4d76..98a48c100 100644 --- a/net/tools/get_server_time/get_server_time.cc +++ b/net/tools/get_server_time/get_server_time.cc
@@ -226,7 +226,8 @@ // printing_log_observer. net::NetLog net_log; PrintingLogObserver printing_log_observer; - net_log.DeprecatedAddObserver(&printing_log_observer, net::NetLog::LOG_ALL); + net_log.DeprecatedAddObserver(&printing_log_observer, + net::NetLogCaptureMode::IncludeSocketBytes()); QuitDelegate delegate; scoped_ptr<net::URLFetcher> fetcher(
diff --git a/net/tools/quic/quic_simple_client.cc b/net/tools/quic/quic_simple_client.cc index 703f22f..e29a7a3 100644 --- a/net/tools/quic/quic_simple_client.cc +++ b/net/tools/quic/quic_simple_client.cc
@@ -15,6 +15,7 @@ #include "net/quic/quic_default_packet_writer.h" #include "net/quic/quic_protocol.h" #include "net/quic/quic_server_id.h" +#include "net/quic/spdy_utils.h" #include "net/spdy/spdy_http_utils.h" #include "net/udp/udp_client_socket.h" @@ -188,8 +189,10 @@ return; } SpdyHeaderBlock header_block; - CreateSpdyHeadersFromHttpRequest(headers, headers.extra_headers, SPDY3, true, - &header_block); + SpdyMajorVersion spdy_version = + SpdyUtils::GetSpdyVersionForQuicVersion(stream->version()); + CreateSpdyHeadersFromHttpRequest(headers, headers.extra_headers, spdy_version, + true, &header_block); stream->SendRequest(header_block, body, fin); stream->set_visitor(this); } @@ -249,7 +252,9 @@ QuicSpdyClientStream* client_stream = static_cast<QuicSpdyClientStream*>(stream); HttpResponseInfo response; - SpdyHeadersToHttpResponse(client_stream->headers(), SPDY3, &response); + SpdyMajorVersion spdy_version = + SpdyUtils::GetSpdyVersionForQuicVersion(client_stream->version()); + SpdyHeadersToHttpResponse(client_stream->headers(), spdy_version, &response); if (response_listener_.get() != nullptr) { response_listener_->OnCompleteResponse( stream->id(), *response.headers, client_stream->data());
diff --git a/net/tools/quic/quic_time_wait_list_manager_test.cc b/net/tools/quic/quic_time_wait_list_manager_test.cc index 7c451a3..1960521 100644 --- a/net/tools/quic/quic_time_wait_list_manager_test.cc +++ b/net/tools/quic/quic_time_wait_list_manager_test.cc
@@ -188,7 +188,7 @@ : connection_id_(connection_id), sequence_number_(number) { } - virtual bool MatchAndExplain( + bool MatchAndExplain( const std::tr1::tuple<const char*, int> packet_buffer, testing::MatchResultListener* /* listener */) const override { FramerVisitorCapturingPublicReset visitor; @@ -206,9 +206,9 @@ kTestPort == packet.client_address.port(); } - virtual void DescribeTo(::std::ostream* os) const override {} + void DescribeTo(::std::ostream* os) const override {} - virtual void DescribeNegationTo(::std::ostream* os) const override {} + void DescribeNegationTo(::std::ostream* os) const override {} private: QuicConnectionId connection_id_;
diff --git a/net/tools/quic/test_tools/quic_test_utils.h b/net/tools/quic/test_tools/quic_test_utils.h index 7cb4516c..e7884680 100644 --- a/net/tools/quic/test_tools/quic_test_utils.h +++ b/net/tools/quic/test_tools/quic_test_utils.h
@@ -43,14 +43,14 @@ class TestSession : public QuicSession { public: TestSession(QuicConnection* connection, const QuicConfig& config); - virtual ~TestSession(); + ~TestSession() override; MOCK_METHOD1(CreateIncomingDataStream, QuicDataStream*(QuicStreamId id)); MOCK_METHOD0(CreateOutgoingDataStream, QuicDataStream*()); void SetCryptoStream(QuicCryptoStream* stream); - virtual QuicCryptoStream* GetCryptoStream() override; + QuicCryptoStream* GetCryptoStream() override; private: QuicCryptoStream* crypto_stream_;
diff --git a/net/udp/udp_net_log_parameters.cc b/net/udp/udp_net_log_parameters.cc index c258823..fb25b4c7 100644 --- a/net/udp/udp_net_log_parameters.cc +++ b/net/udp/udp_net_log_parameters.cc
@@ -16,10 +16,10 @@ base::Value* NetLogUDPDataTranferCallback(int byte_count, const char* bytes, const IPEndPoint* address, - NetLog::LogLevel log_level) { + NetLogCaptureMode capture_mode) { base::DictionaryValue* dict = new base::DictionaryValue(); dict->SetInteger("byte_count", byte_count); - if (NetLog::IsLoggingBytes(log_level)) + if (capture_mode.include_socket_bytes()) dict->SetString("hex_encoded_bytes", base::HexEncode(bytes, byte_count)); if (address) dict->SetString("address", address->ToString()); @@ -27,7 +27,7 @@ } base::Value* NetLogUDPConnectCallback(const IPEndPoint* address, - NetLog::LogLevel /* log_level */) { + NetLogCaptureMode /* capture_mode */) { base::DictionaryValue* dict = new base::DictionaryValue(); dict->SetString("address", address->ToString()); return dict;
diff --git a/net/udp/udp_socket_libevent.cc b/net/udp/udp_socket_libevent.cc index 82597777..2fce83f51 100644 --- a/net/udp/udp_socket_libevent.cc +++ b/net/udp/udp_socket_libevent.cc
@@ -496,7 +496,7 @@ return; } - if (net_log_.IsLogging()) { + if (net_log_.GetCaptureMode().enabled()) { DCHECK(addr_len > 0); DCHECK(addr); @@ -533,7 +533,7 @@ return; } - if (net_log_.IsLogging()) { + if (net_log_.GetCaptureMode().enabled()) { net_log_.AddEvent( NetLog::TYPE_UDP_BYTES_SENT, CreateNetLogUDPDataTranferCallback(result, bytes, address));
diff --git a/net/udp/udp_socket_win.cc b/net/udp/udp_socket_win.cc index b47247a9..8c226f9 100644 --- a/net/udp/udp_socket_win.cc +++ b/net/udp/udp_socket_win.cc
@@ -681,7 +681,7 @@ return; } - if (net_log_.IsLogging()) { + if (net_log_.GetCaptureMode().enabled()) { net_log_.AddEvent( NetLog::TYPE_UDP_BYTES_RECEIVED, CreateNetLogUDPDataTranferCallback(result, bytes, address)); @@ -698,7 +698,7 @@ return; } - if (net_log_.IsLogging()) { + if (net_log_.GetCaptureMode().enabled()) { net_log_.AddEvent( NetLog::TYPE_UDP_BYTES_SENT, CreateNetLogUDPDataTranferCallback(result, bytes, address));
diff --git a/net/url_request/url_request.cc b/net/url_request/url_request.cc index c539bdf0..213307c 100644 --- a/net/url_request/url_request.cc +++ b/net/url_request/url_request.cc
@@ -881,7 +881,7 @@ int URLRequest::Redirect(const RedirectInfo& redirect_info) { // Matches call in NotifyReceivedRedirect. OnCallToDelegateComplete(); - if (net_log_.IsLogging()) { + if (net_log_.GetCaptureMode().enabled()) { net_log_.AddEvent( NetLog::TYPE_URL_REQUEST_REDIRECTED, NetLog::StringCallback("location",
diff --git a/net/url_request/url_request_http_job_unittest.cc b/net/url_request/url_request_http_job_unittest.cc index c47c819..5ca046f 100644 --- a/net/url_request/url_request_http_job_unittest.cc +++ b/net/url_request/url_request_http_job_unittest.cc
@@ -227,7 +227,7 @@ public: // GoogleMock does not appear to play nicely with move-only types like // scoped_ptr, so this forwarding method acts as a workaround. - virtual WebSocketHandshakeStreamBase* CreateBasicStream( + WebSocketHandshakeStreamBase* CreateBasicStream( scoped_ptr<ClientSocketHandle> connection, bool using_proxy) override { // Discard the arguments since we don't need them anyway.
diff --git a/net/url_request/url_request_job.cc b/net/url_request/url_request_job.cc index ce58e6a..106cb856 100644 --- a/net/url_request/url_request_job.cc +++ b/net/url_request/url_request_job.cc
@@ -27,7 +27,7 @@ // Callback for TYPE_URL_REQUEST_FILTERS_SET net-internals event. base::Value* FiltersSetCallback(Filter* filter, - NetLog::LogLevel /* log_level */) { + NetLogCaptureMode /* capture_mode */) { base::DictionaryValue* event_params = new base::DictionaryValue(); event_params->SetString("filters", filter->OrderedFilterList()); return event_params; @@ -698,7 +698,8 @@ } // If logging all bytes is enabled, log the filtered bytes read. - if (rv && request() && request()->net_log().IsLoggingBytes() && + if (rv && request() && + request()->net_log().GetCaptureMode().include_socket_bytes() && filtered_data_len > 0) { request()->net_log().AddByteTransferEvent( NetLog::TYPE_URL_REQUEST_JOB_FILTERED_BYTES_READ, @@ -789,7 +790,8 @@ void URLRequestJob::OnRawReadComplete(int bytes_read) { DCHECK(raw_read_buffer_.get()); // If |filter_| is non-NULL, bytes will be logged after it is applied instead. - if (!filter_.get() && request() && request()->net_log().IsLoggingBytes() && + if (!filter_.get() && request() && + request()->net_log().GetCaptureMode().include_socket_bytes() && bytes_read > 0) { request()->net_log().AddByteTransferEvent( NetLog::TYPE_URL_REQUEST_JOB_BYTES_READ,
diff --git a/net/url_request/url_request_netlog_params.cc b/net/url_request/url_request_netlog_params.cc index a1e8243b..9833fe38 100644 --- a/net/url_request/url_request_netlog_params.cc +++ b/net/url_request/url_request_netlog_params.cc
@@ -10,12 +10,13 @@ namespace net { -base::Value* NetLogURLRequestStartCallback(const GURL* url, - const std::string* method, - int load_flags, - RequestPriority priority, - int64 upload_id, - NetLog::LogLevel /* log_level */) { +base::Value* NetLogURLRequestStartCallback( + const GURL* url, + const std::string* method, + int load_flags, + RequestPriority priority, + int64 upload_id, + NetLogCaptureMode /* capture_mode */) { base::DictionaryValue* dict = new base::DictionaryValue(); dict->SetString("url", url->possibly_invalid_spec()); dict->SetString("method", *method);
diff --git a/net/url_request/url_request_netlog_params.h b/net/url_request/url_request_netlog_params.h index 9ca36bca..d216105ac 100644 --- a/net/url_request/url_request_netlog_params.h +++ b/net/url_request/url_request_netlog_params.h
@@ -26,7 +26,7 @@ int load_flags, RequestPriority priority, int64 upload_id, - NetLog::LogLevel /* log_level */); + NetLogCaptureMode /* capture_mode */); // Attempts to extract the load flags from a Value created by the above // function. On success, sets |load_flags| accordingly and returns true.
diff --git a/net/url_request/url_request_throttler_entry.cc b/net/url_request/url_request_throttler_entry.cc index 1197f6fa..84a9014 100644 --- a/net/url_request/url_request_throttler_entry.cc +++ b/net/url_request/url_request_throttler_entry.cc
@@ -52,10 +52,11 @@ "disable"; // Returns NetLog parameters when a request is rejected by throttling. -base::Value* NetLogRejectedRequestCallback(const std::string* url_id, - int num_failures, - const base::TimeDelta& release_after, - NetLog::LogLevel /* log_level */) { +base::Value* NetLogRejectedRequestCallback( + const std::string* url_id, + int num_failures, + const base::TimeDelta& release_after, + NetLogCaptureMode /* capture_mode */) { base::DictionaryValue* dict = new base::DictionaryValue(); dict->SetString("url", *url_id); dict->SetInteger("num_failures", num_failures);
diff --git a/net/websockets/websocket_channel_test.cc b/net/websockets/websocket_channel_test.cc index 57dd2da..fe49f39 100644 --- a/net/websockets/websocket_channel_test.cc +++ b/net/websockets/websocket_channel_test.cc
@@ -173,7 +173,7 @@ OnFinishOpeningHandshakeCalled(); return CHANNEL_ALIVE; } - virtual ChannelState OnSSLCertificateError( + ChannelState OnSSLCertificateError( scoped_ptr<SSLErrorCallbacks> ssl_error_callbacks, const GURL& url, const SSLInfo& ssl_info,
diff --git a/pdf/instance.h b/pdf/instance.h index bdb52430..daded88 100644 --- a/pdf/instance.h +++ b/pdf/instance.h
@@ -58,36 +58,36 @@ public ControlOwner { public: explicit Instance(PP_Instance instance); - virtual ~Instance(); + ~Instance() override; // pp::Instance implementation. - virtual bool Init(uint32_t argc, - const char* argn[], - const char* argv[]) override; - virtual bool HandleDocumentLoad(const pp::URLLoader& loader) override; - virtual bool HandleInputEvent(const pp::InputEvent& event) override; - virtual void DidChangeView(const pp::View& view) override; - virtual pp::Var GetInstanceObject() override; + bool Init(uint32_t argc, + const char* argn[], + const char* argv[]) override; + bool HandleDocumentLoad(const pp::URLLoader& loader) override; + bool HandleInputEvent(const pp::InputEvent& event) override; + void DidChangeView(const pp::View& view) override; + pp::Var GetInstanceObject() override; // pp::Find_Private implementation. - virtual bool StartFind(const std::string& text, bool case_sensitive) override; - virtual void SelectFindResult(bool forward) override; - virtual void StopFind() override; + bool StartFind(const std::string& text, bool case_sensitive) override; + void SelectFindResult(bool forward) override; + void StopFind() override; // pp::PaintManager::Client implementation. - virtual void OnPaint(const std::vector<pp::Rect>& paint_rects, - std::vector<PaintManager::ReadyRect>* ready, - std::vector<pp::Rect>* pending) override; + void OnPaint(const std::vector<pp::Rect>& paint_rects, + std::vector<PaintManager::ReadyRect>* ready, + std::vector<pp::Rect>* pending) override; // pp::Printing_Dev implementation. - virtual uint32_t QuerySupportedPrintOutputFormats() override; - virtual int32_t PrintBegin( + uint32_t QuerySupportedPrintOutputFormats() override; + int32_t PrintBegin( const PP_PrintSettings_Dev& print_settings) override; - virtual pp::Resource PrintPages( + pp::Resource PrintPages( const PP_PrintPageNumberRange_Dev* page_ranges, uint32_t page_range_count) override; - virtual void PrintEnd() override; - virtual bool IsPrintScalingDisabled() override; + void PrintEnd() override; + bool IsPrintScalingDisabled() override; // pp::Private implementation. virtual pp::Var GetLinkAtPosition(const pp::Point& point); @@ -95,18 +95,18 @@ PP_PdfPrintPresetOptions_Dev* options); // PPP_Selection_Dev implementation. - virtual pp::Var GetSelectedText(bool html) override; + pp::Var GetSelectedText(bool html) override; // WidgetClient_Dev implementation. - virtual void InvalidateWidget(pp::Widget_Dev widget, - const pp::Rect& dirty_rect) override; - virtual void ScrollbarValueChanged(pp::Scrollbar_Dev scrollbar, - uint32_t value) override; - virtual void ScrollbarOverlayChanged(pp::Scrollbar_Dev scrollbar, - bool overlay) override; + void InvalidateWidget(pp::Widget_Dev widget, + const pp::Rect& dirty_rect) override; + void ScrollbarValueChanged(pp::Scrollbar_Dev scrollbar, + uint32_t value) override; + void ScrollbarOverlayChanged(pp::Scrollbar_Dev scrollbar, + bool overlay) override; // pp::Zoom_Dev implementation. - virtual void Zoom(double scale, bool text_only) override; + void Zoom(double scale, bool text_only) override; void ZoomChanged(double factor); // Override. void FlushCallback(int32_t result); @@ -194,8 +194,8 @@ pp::Var* exception); // PreviewModeClient::Client implementation. - virtual void PreviewDocumentLoadComplete() override; - virtual void PreviewDocumentLoadFailed() override; + void PreviewDocumentLoadComplete() override; + void PreviewDocumentLoadFailed() override; // Helper functions for implementing PPP_PDF. void RotateClockwise();
diff --git a/pdf/out_of_process_instance.h b/pdf/out_of_process_instance.h index 62d21c1..091fbc3 100644 --- a/pdf/out_of_process_instance.h +++ b/pdf/out_of_process_instance.h
@@ -45,35 +45,32 @@ public PreviewModeClient::Client { public: explicit OutOfProcessInstance(PP_Instance instance); - virtual ~OutOfProcessInstance(); + ~OutOfProcessInstance() override; // pp::Instance implementation. - virtual bool Init(uint32_t argc, - const char* argn[], - const char* argv[]) override; - virtual void HandleMessage(const pp::Var& message) override; - virtual bool HandleInputEvent(const pp::InputEvent& event) override; - virtual void DidChangeView(const pp::View& view) override; + bool Init(uint32_t argc, const char* argn[], const char* argv[]) override; + void HandleMessage(const pp::Var& message) override; + bool HandleInputEvent(const pp::InputEvent& event) override; + void DidChangeView(const pp::View& view) override; // pp::Find_Private implementation. - virtual bool StartFind(const std::string& text, bool case_sensitive) override; - virtual void SelectFindResult(bool forward) override; - virtual void StopFind() override; + bool StartFind(const std::string& text, bool case_sensitive) override; + void SelectFindResult(bool forward) override; + void StopFind() override; // pp::PaintManager::Client implementation. - virtual void OnPaint(const std::vector<pp::Rect>& paint_rects, - std::vector<PaintManager::ReadyRect>* ready, - std::vector<pp::Rect>* pending) override; + void OnPaint(const std::vector<pp::Rect>& paint_rects, + std::vector<PaintManager::ReadyRect>* ready, + std::vector<pp::Rect>* pending) override; // pp::Printing_Dev implementation. - virtual uint32_t QuerySupportedPrintOutputFormats() override; - virtual int32_t PrintBegin( - const PP_PrintSettings_Dev& print_settings) override; - virtual pp::Resource PrintPages( + uint32_t QuerySupportedPrintOutputFormats() override; + int32_t PrintBegin(const PP_PrintSettings_Dev& print_settings) override; + pp::Resource PrintPages( const PP_PrintPageNumberRange_Dev* page_ranges, uint32_t page_range_count) override; - virtual void PrintEnd() override; - virtual bool IsPrintScalingDisabled() override; + void PrintEnd() override; + bool IsPrintScalingDisabled() override; // pp::Private implementation. virtual pp::Var GetLinkAtPosition(const pp::Point& point); @@ -81,7 +78,7 @@ PP_PdfPrintPresetOptions_Dev* options); // PPP_Selection_Dev implementation. - virtual pp::Var GetSelectedText(bool html) override; + pp::Var GetSelectedText(bool html) override; void FlushCallback(int32_t result); void DidOpen(int32_t result);
diff --git a/ppapi/native_client/src/untrusted/irt_stub/irt_stub.gyp b/ppapi/native_client/src/untrusted/irt_stub/irt_stub.gyp index db1493d..c00cf980 100644 --- a/ppapi/native_client/src/untrusted/irt_stub/irt_stub.gyp +++ b/ppapi/native_client/src/untrusted/irt_stub/irt_stub.gyp
@@ -25,9 +25,6 @@ 'ppapi_plugin_start.c', 'thread_creator.c' ], - 'dependencies': [ - '<(DEPTH)/native_client/tools.gyp:prep_toolchain', - ], }, ], }
diff --git a/ppapi/native_client/src/untrusted/pnacl_irt_shim/pnacl_irt_shim.gyp b/ppapi/native_client/src/untrusted/pnacl_irt_shim/pnacl_irt_shim.gyp index a2e9e1f..946437d9 100644 --- a/ppapi/native_client/src/untrusted/pnacl_irt_shim/pnacl_irt_shim.gyp +++ b/ppapi/native_client/src/untrusted/pnacl_irt_shim/pnacl_irt_shim.gyp
@@ -51,9 +51,6 @@ '-DPNACL_SHIM_AOT', ], }, - 'dependencies': [ - '<(DEPTH)/native_client/tools.gyp:prep_toolchain', - ], }, # Smaller shim library for PNaCl in-browser translation. # Uses an unstable IRT hook interface to get the shim from the IRT itself. @@ -87,9 +84,6 @@ '--strip-debug', ], }, - 'dependencies': [ - '<(DEPTH)/native_client/tools.gyp:prep_toolchain', - ], }, { # Second half of shim library for PNaCl in-browser translation. @@ -109,9 +103,6 @@ 'pnacl_shim.c', ], }, - 'dependencies': [ - '<(DEPTH)/native_client/tools.gyp:prep_toolchain', - ], }, ], }
diff --git a/ppapi/native_client/src/untrusted/pnacl_support_extension/pnacl_support_extension.gyp b/ppapi/native_client/src/untrusted/pnacl_support_extension/pnacl_support_extension.gyp index e8dc67d..573151df 100644 --- a/ppapi/native_client/src/untrusted/pnacl_support_extension/pnacl_support_extension.gyp +++ b/ppapi/native_client/src/untrusted/pnacl_support_extension/pnacl_support_extension.gyp
@@ -20,7 +20,6 @@ ['disable_nacl==0 and disable_pnacl==0 and disable_nacl_untrusted==0', { 'dependencies': [ '../../../../../ppapi/native_client/src/untrusted/pnacl_irt_shim/pnacl_irt_shim.gyp:browser', - '../../../../../native_client/tools.gyp:prep_toolchain', ], 'sources': [ 'pnacl_component_crx_gen.py',
diff --git a/ppapi/ppapi_ipc_nacl.gyp b/ppapi/ppapi_ipc_nacl.gyp index c82a4ec5..3608f80 100644 --- a/ppapi/ppapi_ipc_nacl.gyp +++ b/ppapi/ppapi_ipc_nacl.gyp
@@ -31,7 +31,6 @@ '..', ], 'dependencies': [ - '../native_client/tools.gyp:prep_toolchain', '../base/base_nacl.gyp:base_nacl', '../base/base_nacl.gyp:base_nacl_nonsfi', '../gpu/gpu_nacl.gyp:gpu_ipc_nacl',
diff --git a/ppapi/ppapi_nacl.gyp b/ppapi/ppapi_nacl.gyp index 91a4a40a..aa7b718be 100644 --- a/ppapi/ppapi_nacl.gyp +++ b/ppapi/ppapi_nacl.gyp
@@ -27,9 +27,6 @@ 'cpp/ppp_entrypoints.cc', ], }, - 'dependencies': [ - '<(DEPTH)/native_client/tools.gyp:prep_toolchain', - ], }, { 'target_name': 'ppapi_gles2_lib', @@ -49,15 +46,11 @@ 'lib/gl/gles2/gles2.c', ], }, - 'dependencies': [ - '<(DEPTH)/native_client/tools.gyp:prep_toolchain', - ], }, { 'target_name': 'ppapi_nacl_tests', 'type': 'none', 'dependencies': [ - '<(DEPTH)/native_client/tools.gyp:prep_toolchain', '<(DEPTH)/native_client/src/untrusted/nacl/nacl.gyp:nacl_lib', '<(DEPTH)/native_client/src/untrusted/pthread/pthread.gyp:pthread_lib', 'ppapi_cpp_lib',
diff --git a/ppapi/ppapi_proxy_nacl.gyp b/ppapi/ppapi_proxy_nacl.gyp index c71f9e55..7740a4e7 100644 --- a/ppapi/ppapi_proxy_nacl.gyp +++ b/ppapi/ppapi_proxy_nacl.gyp
@@ -42,7 +42,6 @@ '../ipc/ipc_nacl.gyp:ipc_nacl', '../ipc/ipc_nacl.gyp:ipc_nacl_nonsfi', '../mojo/mojo_nacl.gyp:monacl_codegen', - '../native_client/tools.gyp:prep_toolchain', '../ppapi/ppapi_ipc_nacl.gyp:ppapi_ipc_nacl', '../ppapi/ppapi_shared_nacl.gyp:ppapi_shared_nacl', '../third_party/WebKit/public/blink_headers.gyp:blink_headers',
diff --git a/ppapi/ppapi_shared_nacl.gyp b/ppapi/ppapi_shared_nacl.gyp index 819acc7..05e4aca 100644 --- a/ppapi/ppapi_shared_nacl.gyp +++ b/ppapi/ppapi_shared_nacl.gyp
@@ -31,7 +31,6 @@ '..', ], 'dependencies': [ - '../native_client/tools.gyp:prep_toolchain', '../base/base_nacl.gyp:base_nacl', '../base/base_nacl.gyp:base_nacl_nonsfi', '../gpu/command_buffer/command_buffer_nacl.gyp:gles2_utils_nacl',
diff --git a/remoting/BUILD.gn b/remoting/BUILD.gn index 8409632..02adc68d 100644 --- a/remoting/BUILD.gn +++ b/remoting/BUILD.gn
@@ -19,15 +19,19 @@ deps = [ #"//remoting:remoting_browser_test_resources", "//remoting:remoting_unittests", - - #"//remoting:remoting_webapp", - "//remoting/webapp:html", - #"//remoting:remoting_webapp_unittests", #"//app_remoting_test.gyp:ar_sample_test_driver", #"//app_remoting_webapp.gyp:ar_sample_app", ] + if ((is_linux && !is_chromeos) || is_mac) { + deps += [ + # TODO(gyp) Enable this for Windows once the webapp can be built without + # exceeding the 8k cmd line limit on Windows. + "//remoting/webapp", + ] + } + if (is_win) { deps += [ #"//remoting:remoting_breakpad_tester", @@ -136,10 +140,8 @@ } } -# TODO(GYP) remoting_unittests on Mac/Windows. Currently this fails with a -# duplicate resource error on linking on Windows. Just needs to be checked on -# Mac. -if (!is_win && !is_mac) { +# TODO(GYP) remoting_unittests on Mac. Needs to be tested. +if (!is_mac) { test("remoting_unittests") { # Sources not included in one of the more specific unit_tests deps. sources = [ @@ -186,6 +188,7 @@ deps += [ "//remoting/codec:unit_tests", "//remoting/host:unit_tests", + "//ui/gfx", ] }
diff --git a/remoting/base/BUILD.gn b/remoting/base/BUILD.gn index bb79d52..48f40ef 100644 --- a/remoting/base/BUILD.gn +++ b/remoting/base/BUILD.gn
@@ -29,6 +29,27 @@ ] } +source_set("breakpad") { + sources = [ + "breakpad.h", + "breakpad_linux.cc", + "breakpad_mac.mm", + "breakpad_win.cc", + ] + + configs += [ "//build/config/compiler:wexit_time_destructors" ] + + deps = [ + "//base", + ] + + if (is_mac) { + deps += [ "//breakpad" ] + } else if (is_win) { + deps += [ "//breakpad:breakpad_handler" ] + } +} + source_set("unit_tests") { testonly = true @@ -50,6 +71,7 @@ deps = [ ":base", + ":breakpad", "//base", "//net:test_support", "//testing/gmock", @@ -62,5 +84,4 @@ sources += [ "resources_unittest.cc" ] deps += [ "//breakpad:client" ] } - }
diff --git a/remoting/base/vlog_net_log.cc b/remoting/base/vlog_net_log.cc index db72ad3..39a388a7 100644 --- a/remoting/base/vlog_net_log.cc +++ b/remoting/base/vlog_net_log.cc
@@ -42,7 +42,8 @@ VlogNetLog::VlogNetLog() : observer_(new Observer()) { - DeprecatedAddObserver(observer_.get(), LOG_ALL_BUT_BYTES); + DeprecatedAddObserver(observer_.get(), + net::NetLogCaptureMode::IncludeCookiesAndCredentials()); } VlogNetLog::~VlogNetLog() {
diff --git a/remoting/client/jni/chromoting_jni_instance.cc b/remoting/client/jni/chromoting_jni_instance.cc index 6cfc8870e..33bd2466 100644 --- a/remoting/client/jni/chromoting_jni_instance.cc +++ b/remoting/client/jni/chromoting_jni_instance.cc
@@ -450,10 +450,10 @@ // |client_| must be torn down before |signaling_|. client_.reset(); client_status_logger_.reset(); - client_context_.reset(); video_renderer_.reset(); authenticator_.reset(); signaling_.reset(); + client_context_.reset(); } void ChromotingJniInstance::FetchSecret(
diff --git a/remoting/client/software_video_renderer.cc b/remoting/client/software_video_renderer.cc index 0be8b498..51782e05 100644 --- a/remoting/client/software_video_renderer.cc +++ b/remoting/client/software_video_renderer.cc
@@ -325,7 +325,8 @@ SoftwareVideoRenderer::~SoftwareVideoRenderer() { DCHECK(CalledOnValidThread()); - decode_task_runner_->DeleteSoon(FROM_HERE, core_.release()); + bool result = decode_task_runner_->DeleteSoon(FROM_HERE, core_.release()); + DCHECK(result); } void SoftwareVideoRenderer::OnSessionConfig(
diff --git a/remoting/host/it2me/BUILD.gn b/remoting/host/it2me/BUILD.gn index 965977c..076c21b 100644 --- a/remoting/host/it2me/BUILD.gn +++ b/remoting/host/it2me/BUILD.gn
@@ -45,6 +45,7 @@ "//remoting/host", "//remoting/host/native_messaging", "//remoting/proto", + "//ui/gfx", ] if (enable_webrtc) {
diff --git a/remoting/remoting.gyp b/remoting/remoting.gyp index 6d783c2..d4f1764c4 100644 --- a/remoting/remoting.gyp +++ b/remoting/remoting.gyp
@@ -84,6 +84,7 @@ 'targets': [ { + # GN version: //remoting/base:breakpad 'target_name': 'remoting_breakpad', 'type': 'static_library', 'variables': { 'enable_wexit_time_destructors': 1, },
diff --git a/remoting/remoting_client.gypi b/remoting/remoting_client.gypi index 718d90f..d146658 100644 --- a/remoting/remoting_client.gypi +++ b/remoting/remoting_client.gypi
@@ -49,11 +49,12 @@ }, # end of target 'remoting_client' { + # GN version: //remoting/webapp:html 'target_name': 'remoting_webapp_html', 'type': 'none', 'actions': [ { - # GN version: //remoting/webapp:html + # GN version: //remoting/webapp:main_html 'action_name': 'Build Remoting Webapp main.html', 'inputs': [ 'webapp/build-html.py', @@ -74,6 +75,7 @@ ], }, { + # GN version: //remoting/webapp:wcs_sandbox_html 'action_name': 'Build Remoting Webapp wcs_sandbox.html', 'inputs': [ 'webapp/build-html.py', @@ -90,6 +92,7 @@ ], }, { + # GN version: //remoting/webapp:background_html 'action_name': 'Build Remoting Webapp background.html', 'inputs': [ 'webapp/build-html.py',
diff --git a/remoting/remoting_key_tester.gypi b/remoting/remoting_key_tester.gypi index 7eac507..ba16e417 100644 --- a/remoting/remoting_key_tester.gypi +++ b/remoting/remoting_key_tester.gypi
@@ -96,10 +96,6 @@ '-lppapi_stub', '-lppapi_cpp', ], - 'dependencies': [ - # TODO(mseaborn): Remove need for this (https://crbug.com/456902). - '<(DEPTH)/native_client/tools.gyp:prep_toolchain', - ], }, # end of target 'remoting_key_tester_pexe' ], }]
diff --git a/remoting/remoting_locales.gni b/remoting/remoting_locales.gni index 0bd0536..d4dd4e8 100644 --- a/remoting/remoting_locales.gni +++ b/remoting/remoting_locales.gni
@@ -6,10 +6,11 @@ # See also remoting_locales_with_underscores below. remoting_locales = [ - "am", + #"am", "ar", "bg", - "bn", + + #"bn", "ca", "cs", "da", @@ -20,12 +21,14 @@ "es", "es-419", "et", - "fa", - "fake-bidi", + + #"fa", + #"fake-bidi", "fi", "fil", "fr", - "gu", + + #"gu", "he", "hi", "hr", @@ -33,13 +36,15 @@ "id", "it", "ja", - "kn", + + #"kn", "ko", "lt", "lv", - "ml", - "mr", - "ms", + + #"ml", + #"mr", + #"ms", "nb", "nl", "pl", @@ -51,9 +56,10 @@ "sl", "sr", "sv", - "sw", - "ta", - "te", + + #"sw", + #"ta", + #"te", "th", "tr", "uk", @@ -68,7 +74,8 @@ remoting_locales_with_underscores -= [ "en-GB", "es-419", - "fake-bidi", + + #"fake-bidi", "pt-BR", "pt-PT", "zh-CN", @@ -77,7 +84,8 @@ remoting_locales_with_underscores += [ "en_GB", "es_419", - "fake_bidi", + + #"fake_bidi", "pt_BR", "pt_PT", "zh_CN",
diff --git a/remoting/remoting_nacl.gyp b/remoting/remoting_nacl.gyp index b7c7374..b66a742b 100644 --- a/remoting/remoting_nacl.gyp +++ b/remoting/remoting_nacl.gyp
@@ -74,7 +74,6 @@ 'GOOGLE_PROTOBUF_HOST_ARCH_64_BIT=1' ], 'dependencies': [ - '../native_client/tools.gyp:prep_toolchain', '../third_party/protobuf/protobuf_nacl.gyp:protobuf_lite_nacl', 'proto/chromotocol.gyp:chromotocol_proto_lib', ], @@ -101,7 +100,6 @@ 'dependencies': [ '../base/base_nacl.gyp:base_nacl', '../jingle/jingle_nacl.gyp:jingle_glue_nacl', - '../native_client/tools.gyp:prep_toolchain', '../native_client_sdk/native_client_sdk_untrusted.gyp:nacl_io_untrusted', '../net/net_nacl.gyp:net_nacl', '../third_party/boringssl/boringssl_nacl.gyp:boringssl_nacl', @@ -181,7 +179,6 @@ '../crypto/crypto_nacl.gyp:crypto_nacl', '../jingle/jingle_nacl.gyp:jingle_glue_nacl', '../media/media_nacl.gyp:media_yuv_nacl', - '../native_client/tools.gyp:prep_toolchain', '../native_client_sdk/native_client_sdk_untrusted.gyp:nacl_io_untrusted', '../net/net_nacl.gyp:net_nacl', '../ppapi/native_client/native_client.gyp:ppapi_lib',
diff --git a/remoting/remoting_options.gni b/remoting/remoting_options.gni new file mode 100644 index 0000000..9a62253 --- /dev/null +++ b/remoting/remoting_options.gni
@@ -0,0 +1,33 @@ +# 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. + +# Set this to run the jscompile checks after building the webapp. +run_jscompile = 0 + +# Set this to enable cast mode on the android client. +enable_cast = 0 + +# Set this to use GCD instead of the remoting directory service. +remoting_use_gcd = 0 + +# Enable the multi-process host on Windows by default. +if (is_win) { + remoting_multi_process = 1 +} else { + remoting_multi_process = 0 +} + +remoting_rdp_session = 1 + +branding_path = "../remoting/branding_<(branding)" + +# The ar_service_environment variable is used to define the target +# environment for the app being built. +# The allowed values are dev, test, staging, prod, and prod-testing. +if (is_debug) { + ar_service_environment = "dev" +} else { + # Non-dev builds should default to 'prod'. + ar_service_environment = "prod" +}
diff --git a/remoting/remoting_webapp_files.gypi b/remoting/remoting_webapp_files.gypi index c4274743..2e98b04 100644 --- a/remoting/remoting_webapp_files.gypi +++ b/remoting/remoting_webapp_files.gypi
@@ -72,6 +72,7 @@ 'webapp/base/js/ipc_unittest.js', 'webapp/base/js/protocol_extension_manager_unittest.js', 'webapp/crd/js/apps_v2_migration_unittest.js', + 'webapp/crd/js/client_session_unittest.js', 'webapp/crd/js/desktop_viewport_unittest.js', 'webapp/crd/js/client_session_factory_unittest.js', 'webapp/crd/js/dns_blackhole_checker_unittest.js',
diff --git a/remoting/resources/BUILD.gn b/remoting/resources/BUILD.gn index 05aab92..d28753b 100644 --- a/remoting/resources/BUILD.gn +++ b/remoting/resources/BUILD.gn
@@ -59,7 +59,7 @@ ] + rebase_path(sources_to_verify, root_build_dir) deps = [ - "//remoting/webapp:html", + "//remoting/webapp:main_html", ] # Generates main.html. } } # if false
diff --git a/remoting/webapp/BUILD.gn b/remoting/webapp/BUILD.gn index 67af8ea1..02b6667 100644 --- a/remoting/webapp/BUILD.gn +++ b/remoting/webapp/BUILD.gn
@@ -2,12 +2,49 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -# Keep in sync with 'remoting_webapp_html' target in -# remoting/remoting_client.gypi. +# Keep in sync with targets in remoting/remoting_client.gypi. -import("//remoting/webapp/files.gni") +import("//build/config/features.gni") +import("//remoting/webapp/build_template.gni") -action("html") { +group("webapp") { + deps = [ + ":webapp_v1", + ] + + if (enable_nacl) { + deps += [ ":webapp_v2" ] + } +} + +remoting_webapp("webapp_v1") { + webapp_type = "v1" + output_dir = "remoting.webapp" + zip_path = "remoting-webapp.zip" + extra_files = [] +} + +remoting_webapp("webapp_v2") { + webapp_type = "v2_pnacl" + output_dir = "remoting.webapp.v2" + zip_path = "remoting-webapp.v2.zip" + extra_files = [ + "crd/remoting_client_pnacl.nmf.jinja2", + # TODO(garykac): Get correct path to this. + #"<(PRODUCT_DIR)/remoting_client_plugin_newlib.pexe", + ] +} + +group("html") { + deps = [ + ":background_html", + ":main_html", + ":message_window_html", + ":wcs_sandbox_html", + ] +} + +action("main_html") { script = "build-html.py" inputs = [ remoting_webapp_template_main ] + remoting_webapp_template_files + @@ -17,23 +54,72 @@ "$target_gen_dir/main.html", ] - # Template files are relative to this directory. This passes some template - # files to the script, and the script reads templates from the files on disk. - # They all have to be relative to the same directory. The GYP build made all - # of these relative to the remoting directory, so this does the same. - template_rel_dir = "//remoting" - args = [ rebase_path("$target_gen_dir/main.html", root_build_dir), rebase_path(remoting_webapp_template_main, root_build_dir), ] args += [ "--template-dir", - rebase_path(template_rel_dir, root_build_dir), + rebase_path(remoting_dir, root_build_dir), ] args += [ "--templates" ] + - rebase_path(remoting_webapp_template_files, template_rel_dir) - args += [ "--js" ] + rebase_path(remoting_webapp_crd_main_html_all_js_files, - template_rel_dir) + rebase_path(remoting_webapp_template_files, remoting_dir) + args += [ "--js" ] + + rebase_path(remoting_webapp_crd_main_html_all_js_files, remoting_dir) } -# TODO(GYP) wcs_sandbox.html, background.html, message_window.html + +action("wcs_sandbox_html") { + script = "build-html.py" + + inputs = [ remoting_webapp_template_wcs_sandbox ] + + remoting_webapp_wcs_sandbox_html_all_js_files + + outputs = [ + "$target_gen_dir/wcs_sandbox.html", + ] + + args = [ + rebase_path("$target_gen_dir/wcs_sandbox.html", root_build_dir), + rebase_path(remoting_webapp_template_wcs_sandbox, root_build_dir), + ] + args += + [ "--js" ] + + rebase_path(remoting_webapp_wcs_sandbox_html_all_js_files, remoting_dir) +} + +action("background_html") { + script = "build-html.py" + + inputs = [ remoting_webapp_template_background ] + + remoting_webapp_background_html_all_js_files + + outputs = [ + "$target_gen_dir/background.html", + ] + + args = [ + rebase_path("$target_gen_dir/background.html", root_build_dir), + rebase_path(remoting_webapp_template_background, root_build_dir), + ] + args += [ "--js" ] + rebase_path(remoting_webapp_background_html_all_js_files, + remoting_dir) +} + +action("message_window_html") { + script = "build-html.py" + + inputs = [ remoting_webapp_template_message_window ] + + remoting_webapp_message_window_html_all_js_files + + outputs = [ + "$target_gen_dir/message_window.html", + ] + + args = [ + rebase_path("$target_gen_dir/message_window.html", root_build_dir), + rebase_path(remoting_webapp_template_message_window, root_build_dir), + ] + args += + [ "--js" ] + rebase_path(remoting_webapp_message_window_html_all_js_files, + remoting_dir) +}
diff --git a/remoting/webapp/browser_test/timeout_waiter.js b/remoting/webapp/browser_test/timeout_waiter.js index fd75ada..cb1a3a4 100644 --- a/remoting/webapp/browser_test/timeout_waiter.js +++ b/remoting/webapp/browser_test/timeout_waiter.js
@@ -34,31 +34,32 @@ * @return {Promise} */ browserTest.waitFor = function(predicate, opt_timeout) { - /** - * @param {function():void} fulfill - * @param {function(Error):void} reject - */ - return new Promise(function (fulfill, reject) { - if (opt_timeout === undefined) { - opt_timeout = browserTest.Timeout.DEFAULT; - } + return new Promise( + /** + * @param {function(boolean):void} fulfill + * @param {function(Error):void} reject + */ + function (fulfill, reject) { + if (opt_timeout === undefined) { + opt_timeout = browserTest.Timeout.DEFAULT; + } - var timeout = /** @type {number} */ (opt_timeout); - var end = Number(Date.now()) + timeout; - var testPredicate = function() { - if (predicate.evaluate()) { - console.log(predicate.description() + ' satisfied.'); - fulfill(true); - } else if (Date.now() >= end) { - reject(new Error('Timed out (' + opt_timeout + 'ms) waiting for ' + - predicate.description())); - } else { - console.log(predicate.description() + ' not yet satisfied.'); - window.setTimeout(testPredicate, 500); - } - }; - testPredicate(); - }); + var timeout = /** @type {number} */ (opt_timeout); + var end = Number(Date.now()) + timeout; + var testPredicate = function() { + if (predicate.evaluate()) { + console.log(predicate.description() + ' satisfied.'); + fulfill(true); + } else if (Date.now() >= end) { + reject(new Error('Timed out (' + opt_timeout + 'ms) waiting for ' + + predicate.description())); + } else { + console.log(predicate.description() + ' not yet satisfied.'); + window.setTimeout(testPredicate, 500); + } + }; + testPredicate(); + }); }; /**
diff --git a/remoting/webapp/build_template.gni b/remoting/webapp/build_template.gni new file mode 100644 index 0000000..d1e2265 --- /dev/null +++ b/remoting/webapp/build_template.gni
@@ -0,0 +1,95 @@ +# 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. + +# Keep in sync with 'remoting_webapp' target in remoting/remoting_client.gypi. + +import("//remoting/remoting_locales.gni") +import("//remoting/remoting_options.gni") +import("//remoting/remoting_version.gni") +import("//remoting/webapp/files.gni") + +# The base remoting directory that is used as the root directory for file +# references. Many of the scripts rely on the files being specified relative +# to this directory. +remoting_dir = "//remoting" + +template("remoting_webapp") { + locales_listfile = target_name + "_locales" + listfile = "$target_gen_dir/${target_name}_locales.txt" + listfile_rel = rebase_path(listfile, root_build_dir) + + action(locales_listfile) { + script = "../tools/build/remoting_localize.py" + + inputs = [] + outputs = [ + listfile, + ] + + args = [ + "--locale_output", + rebase_path(webapp_locale_dir, root_build_dir) + + "/@{json_suffix}/messages.json", + "--locales_listfile", + listfile_rel, + ] + args += remoting_locales + } + + action(target_name) { + script = "build-webapp.py" + + webapp_type = invoker.webapp_type + output_dir = invoker.output_dir + zip_path = invoker.zip_path + extra_files = invoker.extra_files + + inputs = [] + outputs = [ + "$target_gen_dir/$zip_path", + ] + + deps = [ + ":html", + ":$locales_listfile", + "//remoting/resources", + ] + + buildtype = "Dev" + if (!is_debug) { + if (is_official_build) { + buildtype = "Official" + } else { + buildtype = "Release" + } + } + + generated_html_files = [ + "$target_gen_dir/background.html", + "$target_gen_dir/main.html", + "$target_gen_dir/message_window.html", + "$target_gen_dir/wcs_sandbox.html", + ] + + args = [ + buildtype, + version_full, + output_dir, + zip_path, + rebase_path("crd/manifest.json.jinja2", root_build_dir), + webapp_type, + ] + args += rebase_path(generated_html_files, root_build_dir) + args += rebase_path(remoting_webapp_crd_files, root_build_dir) + args += rebase_path(extra_files, root_build_dir) + args += [ + "--locales_listfile", + listfile_rel, + ] + args += [ + "--use_gcd", + "$remoting_use_gcd", + ] + } +}
diff --git a/remoting/webapp/crd/js/app_launcher.js b/remoting/webapp/crd/js/app_launcher.js index e5f158d..fc28ab7 100644 --- a/remoting/webapp/crd/js/app_launcher.js +++ b/remoting/webapp/crd/js/app_launcher.js
@@ -52,38 +52,41 @@ remoting.V1AppLauncher.prototype.launch = function(opt_launchArgs) { var url = base.urlJoin('main.html', opt_launchArgs); - /** - * @param {function(*=):void} resolve - * @param {function(*=):void} reject - */ - return new Promise(function(resolve, reject) { - chrome.tabs.create({ url: url, selected: true }, - /** @param {chrome.Tab} tab The created tab. */ - function(tab) { - if (!tab) { - reject(new Error(chrome.runtime.lastError.message)); - } else { - resolve(String(tab.id)); - } - }); - }); + return new Promise( + /** + * @param {function(*=):void} resolve + * @param {function(*=):void} reject + */ + function(resolve, reject) { + chrome.tabs.create({ url: url, selected: true }, + /** @param {chrome.Tab} tab The created tab. */ + function(tab) { + if (!tab) { + reject(new Error(chrome.runtime.lastError.message)); + } else { + resolve(String(tab.id)); + } + }); + }); }; remoting.V1AppLauncher.prototype.close = function(id) { - /** - * @param {function(*=):void} resolve - * @param {function(*=):void} reject - */ - return new Promise(function(resolve, reject) { - /** @param {chrome.Tab} tab The retrieved tab. */ - chrome.tabs.get(id, function(tab) { - if (!tab) { - reject(new Error(chrome.runtime.lastError.message)); - } else { - chrome.tabs.remove(tab.id, /** @type {function(*=):void} */ (resolve)); - } - }); - }); + return new Promise( + /** + * @param {function(*=):void} resolve + * @param {function(*=):void} reject + */ + function(resolve, reject) { + chrome.tabs.get(id, + /** @param {chrome.Tab} tab The retrieved tab. */ + function(tab) { + if (!tab) { + reject(new Error(chrome.runtime.lastError.message)); + } else { + chrome.tabs.remove(tab.id, /** function(*=):void */ (resolve)); + } + }); + }); }; @@ -110,49 +113,51 @@ remoting.V2AppLauncher.prototype.launch = function(opt_launchArgs) { var url = base.urlJoin(APP_MAIN_URL, opt_launchArgs); - /** - * @param {function(*=):void} resolve - * @param {function(*=):void} reject - */ - return new Promise(function(resolve, reject) { - var START_FULLSCREEN = 'start-fullscreen'; - /** @param {Object} values */ - var onValues = function(values) { - /** @type {string} */ - var state = values[START_FULLSCREEN] ? 'fullscreen' : 'normal'; - chrome.app.window.create(url, { - 'width': 800, - 'height': 600, - 'frame': 'none', - 'id': String(getNextWindowId()), - 'state': state - }, - /** @param {AppWindow=} appWindow */ - function(appWindow) { - if (!appWindow) { - reject(new Error(chrome.runtime.lastError.message)); - } else { - resolve(appWindow.id); - } - }); - }; - chrome.storage.local.get(START_FULLSCREEN, onValues); - }); + return new Promise( + /** + * @param {function(*=):void} resolve + * @param {function(*=):void} reject + */ + function(resolve, reject) { + var START_FULLSCREEN = 'start-fullscreen'; + /** @param {Object} values */ + var onValues = function(values) { + /** @type {string} */ + var state = values[START_FULLSCREEN] ? 'fullscreen' : 'normal'; + chrome.app.window.create(url, { + 'width': 800, + 'height': 600, + 'frame': 'none', + 'id': String(getNextWindowId()), + 'state': state + }, + /** @param {AppWindow=} appWindow */ + function(appWindow) { + if (!appWindow) { + reject(new Error(chrome.runtime.lastError.message)); + } else { + resolve(appWindow.id); + } + }); + }; + chrome.storage.local.get(START_FULLSCREEN, onValues); + }); }; remoting.V2AppLauncher.prototype.close = function(id) { - /** - * @param {function(*=):void} resolve - * @param {function(*=):void} reject - */ - return new Promise(function(resolve, reject) { - var appWindow = chrome.app.window.get(id); - if (!appWindow) { - return Promise.reject(new Error(chrome.runtime.lastError.message)); - } - appWindow.onClosed.addListener(resolve); - appWindow.close(); - }); + return new Promise( + /** + * @param {function(*=):void} resolve + * @param {function(*=):void} reject + */ + function(resolve, reject) { + var appWindow = chrome.app.window.get(id); + if (!appWindow) { + return Promise.reject(new Error(chrome.runtime.lastError.message)); + } + appWindow.onClosed.addListener(resolve); + appWindow.close(); + }); }; /**
diff --git a/remoting/webapp/crd/js/client_plugin_host_desktop_impl.js b/remoting/webapp/crd/js/client_plugin_host_desktop_impl.js index 74ba989..94ed9d27 100644 --- a/remoting/webapp/crd/js/client_plugin_host_desktop_impl.js +++ b/remoting/webapp/crd/js/client_plugin_host_desktop_impl.js
@@ -15,7 +15,7 @@ 'use strict'; /** - * @param {remoting.ClientPluginImpl} plugin + * @param {remoting.ClientPlugin} plugin * @param {function(Object):void} postMessageCallback Callback to post a message * to the Client Plugin. *
diff --git a/remoting/webapp/crd/js/client_plugin_impl.js b/remoting/webapp/crd/js/client_plugin_impl.js index b441ef6d..6248956 100644 --- a/remoting/webapp/crd/js/client_plugin_impl.js +++ b/remoting/webapp/crd/js/client_plugin_impl.js
@@ -83,11 +83,12 @@ /** @type {remoting.ClientPluginImpl} */ var that = this; - /** @param {Event} event Message event from the plugin. */ - this.plugin_.addEventListener('message', function(event) { - that.handleMessage_( - /** @type {remoting.ClientPluginMessage} */ (event.data)); - }, false); + this.plugin_.addEventListener('message', + /** @param {Event} event Message event from the plugin. */ + function(event) { + that.handleMessage_( + /** @type {remoting.ClientPluginMessage} */ (event.data)); + }, false); if (remoting.settings.CLIENT_PLUGIN_TYPE == 'native') { window.setTimeout(this.showPluginForClickToPlay_.bind(this), 500);
diff --git a/remoting/webapp/crd/js/client_session.js b/remoting/webapp/crd/js/client_session.js index 2737658..8a6bdb87 100644 --- a/remoting/webapp/crd/js/client_session.js +++ b/remoting/webapp/crd/js/client_session.js
@@ -302,10 +302,6 @@ var state = error.isNone() ? remoting.ClientSession.State.CLOSED : remoting.ClientSession.State.FAILED; - - // The plugin won't send a state change notification, so we explicitly log - // the fact that the connection has closed. - this.logToServer_.logClientSessionStateChange(state, error); this.error_ = error; this.setState_(state); }; @@ -496,6 +492,7 @@ var finishedStates = [ remoting.ClientSession.State.CLOSED, remoting.ClientSession.State.FAILED, + remoting.ClientSession.State.CONNECTION_CANCELED, remoting.ClientSession.State.CONNECTION_DROPPED ]; return finishedStates.indexOf(this.getState()) !== -1; @@ -557,9 +554,12 @@ this.listener_.onDisconnected(); break; + case remoting.ClientSession.State.CONNECTION_CANCELED: case remoting.ClientSession.State.FAILED: error = this.getError(); - console.error('Connection failed: ' + error.toString()); + if (!error.isNone()) { + console.error('Connection failed: ' + error.toString()); + } this.listener_.onConnectionFailed(error); break;
diff --git a/remoting/webapp/crd/js/client_session_factory_unittest.js b/remoting/webapp/crd/js/client_session_factory_unittest.js index f0e884c..819378c1 100644 --- a/remoting/webapp/crd/js/client_session_factory_unittest.js +++ b/remoting/webapp/crd/js/client_session_factory_unittest.js
@@ -6,18 +6,12 @@ 'use strict'; -var originalPluginFactory; -var originalIdentity; -var originalSettings; - -/** @type {remoting.MockSignalStrategy} */ -var mockSignalStrategy; +/** @type {remoting.MockConnection} */ +var mockConnection; /** @type {remoting.ClientSessionFactory} */ var factory; /** @type {remoting.ClientSession.EventHandler} */ var listener; -/** @type {sinon.TestStub} */ -var createSignalStrategyStub; /** * @constructor @@ -31,69 +25,70 @@ QUnit.module('ClientSessionFactory', { beforeEach: function() { - originalPluginFactory = remoting.ClientPlugin.factory; - remoting.ClientPlugin.factory = new remoting.MockClientPluginFactory(); - - mockSignalStrategy = new remoting.MockSignalStrategy( - 'jid', remoting.SignalStrategy.Type.XMPP); - createSignalStrategyStub = sinon.stub(remoting.SignalStrategy, 'create'); - createSignalStrategyStub.returns(mockSignalStrategy); - listener = new SessionListener(); - - originalIdentity = remoting.identity; - remoting.identity = new remoting.Identity(); chromeMocks.activate(['identity']); chromeMocks.identity.mock$setToken('fake_token'); - originalSettings = remoting.settings; - remoting.settings = new remoting.Settings(); - - remoting.identity.getUserInfo = function() { - return { email: 'email', userName: 'userName'}; - }; - + mockConnection = new remoting.MockConnection(); + listener = new SessionListener(); factory = new remoting.ClientSessionFactory( - document.createElement('div'), ['fake_capability']); + document.createElement('div'), + [remoting.ClientSession.Capability.VIDEO_RECORDER]); }, afterEach: function() { - remoting.settings = originalSettings; - remoting.identity = originalIdentity; + mockConnection.restore(); chromeMocks.restore(); - remoting.identity = null; - remoting.ClientPlugin.factory = originalPluginFactory; - createSignalStrategyStub.restore(); } }); QUnit.test('createSession() should return a remoting.ClientSession', function(assert) { - - mockSignalStrategy.connect = function() { - mockSignalStrategy.setStateForTesting( - remoting.SignalStrategy.State.CONNECTED); - }; - return factory.createSession(listener).then( function(/** remoting.ClientSession */ session){ assert.ok(session instanceof remoting.ClientSession); + assert.ok( + mockConnection.plugin().hasCapability( + remoting.ClientSession.Capability.VIDEO_RECORDER), + 'Capability is set correctly.'); }); }); QUnit.test('createSession() should reject on signal strategy failure', function(assert) { - + var mockSignalStrategy = mockConnection.signalStrategy(); mockSignalStrategy.connect = function() { - mockSignalStrategy.setStateForTesting(remoting.SignalStrategy.State.FAILED); + Promise.resolve().then(function () { + mockSignalStrategy.setStateForTesting( + remoting.SignalStrategy.State.FAILED); + }); }; - var signalStrategyDispose = - /** @type {sinon.Spy} */ (sinon.spy(mockSignalStrategy, 'dispose')); + var signalStrategyDispose = sinon.stub(mockSignalStrategy, 'dispose'); return factory.createSession(listener).then( - assert.ok.bind(assert, false, 'Expect createSession to fail') + assert.ok.bind(assert, false, 'Expect createSession() to fail.') ).catch(function(/** remoting.Error */ error) { - assert.ok(signalStrategyDispose.called); - assert.equal(error.getDetail(), 'setStateForTesting'); + assert.ok( + signalStrategyDispose.called, 'SignalStrategy is disposed on failure.'); + assert.equal(error.getDetail(), 'setStateForTesting', + 'Error message is set correctly.'); + }); +}); + +QUnit.test('createSession() should reject on plugin initialization failure', + function(assert) { + var mockSignalStrategy = mockConnection.signalStrategy(); + var plugin = mockConnection.plugin(); + plugin.mock$initializationResult = false; + + var signalStrategyDispose = sinon.stub(mockSignalStrategy, 'dispose'); + + return factory.createSession(listener).then(function() { + assert.ok(false, 'Expect createSession() to fail.'); + }).catch(function(/** remoting.Error */ error) { + assert.ok( + signalStrategyDispose.called, 'SignalStrategy is disposed on failure.'); + assert.ok(error.hasTag(remoting.Error.Tag.MISSING_PLUGIN), + 'Initialization failed with MISSING_PLUGIN.'); }); });
diff --git a/remoting/webapp/crd/js/client_session_unittest.js b/remoting/webapp/crd/js/client_session_unittest.js new file mode 100644 index 0000000..503b83d --- /dev/null +++ b/remoting/webapp/crd/js/client_session_unittest.js
@@ -0,0 +1,177 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +(function() { + +'use strict'; + +/** @type {remoting.MockConnection} */ +var mockConnection; +/** @type {remoting.ClientSession} */ +var session; +/** @type {remoting.ClientSession.EventHandler} */ +var listener; +/** @type {sinon.TestStub} */ +var logToServerStub; + +/** + * @constructor + * @implements {remoting.ClientSession.EventHandler} + */ +var SessionListener = function() {}; +SessionListener.prototype.onConnectionFailed = function(error) {}; +SessionListener.prototype.onConnected = function(connectionInfo) {}; +SessionListener.prototype.onDisconnected = function() {}; +SessionListener.prototype.onError = function(error) {}; + +/** + * @param {remoting.ClientSession.ConnectionError=} opt_error + * @return {Promise} + */ +function connect(opt_error) { + var deferred = new base.Deferred(); + var host = new remoting.Host('fake_hostId'); + host.jabberId = 'fake_jid'; + + var plugin = mockConnection.plugin(); + var State = remoting.ClientSession.State; + + plugin.mock$onConnect().then(function() { + plugin.mock$setConnectionStatus(State.CONNECTING); + }).then(function() { + var status = (opt_error) ? State.FAILED : State.CONNECTED; + plugin.mock$setConnectionStatus(status, opt_error); + }); + + session.connect(host, new remoting.CredentialsProvider({ + pairingInfo: { clientId: 'fake_clientId', sharedSecret: 'fake_secret' } + })); + + listener.onConnected = function() { + deferred.resolve(); + }; + + listener.onConnectionFailed = function(/** remoting.Error */ error) { + deferred.reject(error); + }; + + return deferred.promise(); +} + +QUnit.module('ClientSession', { + beforeEach: function() { + chromeMocks.activate(['identity']); + chromeMocks.identity.mock$setToken('fake_token'); + + mockConnection = new remoting.MockConnection(); + listener = new SessionListener(); + + var sessionFactory = new remoting.ClientSessionFactory( + document.createElement('div'), ['fake_capability']); + + return sessionFactory.createSession(listener).then(function(clientSession) { + session = clientSession; + logToServerStub = + sinon.stub(session.getLogger(), 'logClientSessionStateChange'); + }); + }, + afterEach: function() { + session.dispose(); + mockConnection.restore(); + chromeMocks.restore(); + } +}); + +QUnit.test('should raise CONNECTED event on connected', function(assert) { + return connect().then(function(){ + assert.ok(true, 'Expect session to connect.'); + }); +}); + +QUnit.test('onOutgoingIq() should send Iq to signalStrategy', function(assert) { + var sendMessage = sinon.stub(mockConnection.signalStrategy(), 'sendMessage'); + return connect().then(function(){ + session.onOutgoingIq('sample message'); + assert.ok(sendMessage.calledWith('sample message')); + }); +}); + +QUnit.test('should foward Iq from signalStrategy to plugin', function(assert) { + var onIncomingIq = sinon.stub(mockConnection.plugin(), 'onIncomingIq'); + return connect().then(function() { + var stanza = new DOMParser() + .parseFromString('<iq>sample</iq>', 'text/xml') + .firstElementChild; + mockConnection.signalStrategy().mock$onIncomingStanza(stanza); + assert.ok(onIncomingIq.calledWith('<iq>sample</iq>')); + }); +}); + +QUnit.test('logHostOfflineErrors(false) should suppress offline errors', + function(assert) { + + session.logHostOfflineErrors(false); + + var PluginError = remoting.ClientSession.ConnectionError; + var State = remoting.ClientSession.State; + + return connect(PluginError.HOST_IS_OFFLINE).then(function() { + assert.ok(false, 'Expect connection to fail'); + }).catch(function(/** remoting.Error */ error) { + assert.ok(error.hasTag(remoting.Error.Tag.HOST_IS_OFFLINE)); + assert.equal(logToServerStub.args[1][0], State.CONNECTION_CANCELED); + var errorLogged = /** @type {remoting.Error} */(logToServerStub.args[1][1]); + assert.equal(errorLogged.getTag(), remoting.Error.Tag.HOST_IS_OFFLINE); + + }); +}); + +QUnit.test('disconnect() should raise the CLOSED event', function(assert) { + return connect().then(function() { + var onDisconnected = sinon.stub(listener, 'onDisconnected'); + session.disconnect(remoting.Error.none()); + assert.equal(onDisconnected.callCount, 1); + }); +}); + +QUnit.test( + 'Connection error after CONNECTED should raise the CONNECTION_DROPPED event', + function(assert) { + + var State = remoting.ClientSession.State; + + return connect().then(function() { + var onError = sinon.stub(listener, 'onError'); + session.disconnect(new remoting.Error(remoting.Error.Tag.P2P_FAILURE)); + assert.equal(onError.callCount, 1); + assert.equal(logToServerStub.args[2][0], State.CONNECTION_DROPPED); + }); +}); + +QUnit.test( + 'Connection error before CONNECTED should raise the CONNECTION_FAILED event', + function(assert) { + + session.logHostOfflineErrors(true); + + var PluginError = remoting.ClientSession.ConnectionError; + var State = remoting.ClientSession.State; + + return connect(PluginError.SESSION_REJECTED).then(function() { + assert.ok(false, 'Expect connection to fail'); + }).catch(function(/** remoting.Error */ error) { + assert.ok(error.hasTag(remoting.Error.Tag.INVALID_ACCESS_CODE)); + assert.equal(logToServerStub.args[1][0], State.FAILED); + var errorLogged = /** @type {remoting.Error} */(logToServerStub.args[1][1]); + assert.equal(errorLogged.getTag(), remoting.Error.Tag.INVALID_ACCESS_CODE); + }); +}); + +QUnit.test('dispose() should dispose the plugin', function(assert) { + var pluginDispose = sinon.stub(mockConnection.plugin(), 'dispose'); + session.dispose(); + assert.equal(pluginDispose.callCount, 1); +}); + +})();
diff --git a/remoting/webapp/crd/js/dns_blackhole_checker_unittest.js b/remoting/webapp/crd/js/dns_blackhole_checker_unittest.js index 601157b..46abdd4f 100644 --- a/remoting/webapp/crd/js/dns_blackhole_checker_unittest.js +++ b/remoting/webapp/crd/js/dns_blackhole_checker_unittest.js
@@ -37,7 +37,7 @@ onStateChange = sinon.spy(); onIncomingStanzaCallback = sinon.spy(); signalStrategy = new remoting.MockSignalStrategy(); - sinon.spy(signalStrategy, 'connect'); + sinon.stub(signalStrategy, 'connect', base.doNothing); checker = new remoting.DnsBlackholeChecker(signalStrategy); checker.setStateChangedCallback(onStateChange);
diff --git a/remoting/webapp/crd/js/host_installer.js b/remoting/webapp/crd/js/host_installer.js index 3ff1013b..c8a15a3 100644 --- a/remoting/webapp/crd/js/host_installer.js +++ b/remoting/webapp/crd/js/host_installer.js
@@ -47,31 +47,32 @@ // Always do a fresh check as we don't get notified when the host is // uninstalled. - /** @param {function(*=):void} resolve */ - return new Promise(function(resolve) { - // TODO(kelvinp): Use different native messaging ports for the Me2me host - // vs It2MeHost. - /** @type {chrome.runtime.Port} */ - var port = - chrome.runtime.connectNative('com.google.chrome.remote_assistance'); + return new Promise( + /** @param {function(*=):void} resolve */ + function(resolve) { + // TODO(kelvinp): Use different native messaging ports for the Me2me + // host vs It2MeHost. + /** @type {chrome.runtime.Port} */ + var port = + chrome.runtime.connectNative('com.google.chrome.remote_assistance'); - function onMessage() { - port.onDisconnect.removeListener(onDisconnected); - port.onMessage.removeListener(onMessage); - port.disconnect(); - resolve(true); - } + function onMessage() { + port.onDisconnect.removeListener(onDisconnected); + port.onMessage.removeListener(onMessage); + port.disconnect(); + resolve(true); + } - function onDisconnected() { - port.onDisconnect.removeListener(onDisconnected); - port.onMessage.removeListener(onMessage); - resolve(false); - } + function onDisconnected() { + port.onDisconnect.removeListener(onDisconnected); + port.onMessage.removeListener(onMessage); + resolve(false); + } - port.onDisconnect.addListener(onDisconnected); - port.onMessage.addListener(onMessage); - port.postMessage({type: 'hello'}); - }); + port.onDisconnect.addListener(onDisconnected); + port.onMessage.addListener(onMessage); + port.postMessage({type: 'hello'}); + }); }; /** @type {Object<string,string>} */
diff --git a/remoting/webapp/crd/js/host_list.js b/remoting/webapp/crd/js/host_list.js index 3b130ec..1678464 100644 --- a/remoting/webapp/crd/js/host_list.js +++ b/remoting/webapp/crd/js/host_list.js
@@ -48,7 +48,7 @@ /** @private {HTMLElement} */ this.loadingIndicator_ = loadingIndicator; /** @private */ - this.onError_ = onError; + this.onError_ = remoting.Error.handler(onError); /** @private */ this.handleConnect_ = handleConnect; @@ -152,8 +152,11 @@ that.lastError_ = error; onDone(false); }; - remoting.hostListApi.get(this.onHostListResponse_.bind(this, onDone), - onError); + remoting.hostListApi.get().then(function(hosts) { + onDone(that.onHostListResponse_(hosts)); + }).catch( + remoting.Error.handler(onError) + ); }; /** @@ -161,18 +164,17 @@ * include a JSON-encoded list of host descriptions, which we display if we're * able to successfully parse it. * - * @param {function(boolean):void} onDone The callback passed to |refresh|. * @param {Array<remoting.Host>} hosts The list of hosts for the user. - * @return {void} Nothing. + * @return {boolean} * @private */ -remoting.HostList.prototype.onHostListResponse_ = function(onDone, hosts) { +remoting.HostList.prototype.onHostListResponse_ = function(hosts) { this.lastError_ = remoting.Error.none(); this.hosts_ = hosts; this.sortHosts_(); this.save_(); this.loadingIndicator_.classList.remove('loading'); - onDone(true); + return true; }; /** @@ -304,8 +306,8 @@ if (index != -1) { this.hostTableEntries_.splice(index, 1); } - remoting.hostListApi.remove(hostTableEntry.host.hostId, base.doNothing, - this.onError_); + remoting.hostListApi.remove(hostTableEntry.host.hostId). + catch(this.onError_); }; /** @@ -324,9 +326,8 @@ remoting.hostListApi.put(hostTableEntry.host.hostId, hostTableEntry.host.hostName, - hostTableEntry.host.publicKey, - function() {}, - this.onError_); + hostTableEntry.host.publicKey). + catch(this.onError_); }; /** @@ -347,13 +348,14 @@ return; } - var onRemoved = function() { - that.refresh(function() { - that.display(); - onDone(); - }); - }; - remoting.hostListApi.remove(hostId, onRemoved, this.onError_); + remoting.hostListApi.remove(hostId). + then(function() { + that.refresh(function() { + that.display(); + onDone(); + }); + }). + catch(this.onError_); }; /**
diff --git a/remoting/webapp/crd/js/host_list_api.js b/remoting/webapp/crd/js/host_list_api.js index acdff97..9b63ccb 100644 --- a/remoting/webapp/crd/js/host_list_api.js +++ b/remoting/webapp/crd/js/host_list_api.js
@@ -28,31 +28,28 @@ /** * Fetch the list of hosts for a user. * - * @param {function(Array<remoting.Host>):void} onDone - * @param {function(!remoting.Error):void} onError + * @return {!Promise<!Array<!remoting.Host>>} */ -remoting.HostListApi.prototype.get = function(onDone, onError) { +remoting.HostListApi.prototype.get = function() { }; /** * Update the information for a host. * - * @param {function():void} onDone - * @param {function(!remoting.Error):void} onError * @param {string} hostId * @param {string} hostName * @param {string} hostPublicKey + * @return {!Promise<void>} */ remoting.HostListApi.prototype.put = - function(hostId, hostName, hostPublicKey, onDone, onError) { + function(hostId, hostName, hostPublicKey) { }; /** * Delete a host. * - * @param {function():void} onDone - * @param {function(!remoting.Error):void} onError * @param {string} hostId + * @return {!Promise<void>} */ -remoting.HostListApi.prototype.remove = function(hostId, onDone, onError) { +remoting.HostListApi.prototype.remove = function(hostId) { };
diff --git a/remoting/webapp/crd/js/host_list_api_impl.js b/remoting/webapp/crd/js/host_list_api_impl.js index 55b4868..9cfff10 100644 --- a/remoting/webapp/crd/js/host_list_api_impl.js +++ b/remoting/webapp/crd/js/host_list_api_impl.js
@@ -56,39 +56,22 @@ }); }; -/** - * Fetch the list of hosts for a user. - * - * @param {function(Array<remoting.Host>):void} onDone - * @param {function(!remoting.Error):void} onError - */ -remoting.HostListApiImpl.prototype.get = function(onDone, onError) { - /** @type {function(!remoting.Xhr.Response):void} */ - var parseHostListResponse = - this.parseHostListResponse_.bind(this, onDone, onError); - /** @param {string} token */ - var onToken = function(token) { - new remoting.Xhr({ - method: 'GET', - url: remoting.settings.DIRECTORY_API_BASE_URL + '/@me/hosts', - oauthToken: token - }).start().then(parseHostListResponse); - }; - remoting.identity.getToken().then(onToken, remoting.Error.handler(onError)); +/** @override */ +remoting.HostListApiImpl.prototype.get = function() { + var that = this; + return new remoting.Xhr({ + method: 'GET', + url: remoting.settings.DIRECTORY_API_BASE_URL + '/@me/hosts', + useIdentity: true + }).start().then(function(/** !remoting.Xhr.Response */ response) { + return that.parseHostListResponse_(response); + }); }; -/** - * Update the information for a host. - * - * @param {function():void} onDone - * @param {function(!remoting.Error):void} onError - * @param {string} hostId - * @param {string} hostName - * @param {string} hostPublicKey - */ +/** @override */ remoting.HostListApiImpl.prototype.put = - function(hostId, hostName, hostPublicKey, onDone, onError) { - new remoting.Xhr({ + function(hostId, hostName, hostPublicKey) { + return new remoting.Xhr({ method: 'PUT', url: remoting.settings.DIRECTORY_API_BASE_URL + '/@me/hosts/' + hostId, jsonContent: { @@ -99,23 +82,17 @@ } }, useIdentity: true - }).start().then(remoting.HostListApiImpl.defaultResponse_(onDone, onError)); + }).start().then(remoting.HostListApiImpl.defaultResponse_()); }; -/** - * Delete a host. - * - * @param {function():void} onDone - * @param {function(!remoting.Error):void} onError - * @param {string} hostId - */ -remoting.HostListApiImpl.prototype.remove = function(hostId, onDone, onError) { - new remoting.Xhr({ +/** @override */ +remoting.HostListApiImpl.prototype.remove = function(hostId) { + return new remoting.Xhr({ method: 'DELETE', url: remoting.settings.DIRECTORY_API_BASE_URL + '/@me/hosts/' + hostId, useIdentity: true }).start().then(remoting.HostListApiImpl.defaultResponse_( - onDone, onError, [remoting.Error.Tag.NOT_FOUND])); + [remoting.Error.Tag.NOT_FOUND])); }; /** @@ -123,19 +100,17 @@ * include a JSON-encoded list of host descriptions, which is parsed and * passed to the callback. * - * @param {function(Array<remoting.Host>):void} onDone - * @param {function(!remoting.Error):void} onError * @param {!remoting.Xhr.Response} response + * @return {!Array<!remoting.Host>} * @private */ -remoting.HostListApiImpl.prototype.parseHostListResponse_ = - function(onDone, onError, response) { +remoting.HostListApiImpl.prototype.parseHostListResponse_ = function(response) { if (response.status == 200) { var obj = /** @type {{data: {items: Array}}} */ (base.jsonParseSafe(response.getText())); if (!obj || !obj.data) { console.error('Invalid "hosts" response from server.'); - onError(remoting.Error.unexpected()); + throw remoting.Error.unexpected(); } else { var items = obj.data.items || []; var hosts = items.map( @@ -153,38 +128,33 @@ base.getStringAttr(item, 'hostOfflineReason', ''); return host; }); - onDone(hosts); + return hosts; } } else { - onError(remoting.Error.fromHttpStatus(response.status)); + throw remoting.Error.fromHttpStatus(response.status); } }; /** * Generic success/failure response proxy. * - * @param {function():void} onDone - * @param {function(!remoting.Error):void} onError * @param {Array<remoting.Error.Tag>=} opt_ignoreErrors * @return {function(!remoting.Xhr.Response):void} * @private */ -remoting.HostListApiImpl.defaultResponse_ = function( - onDone, onError, opt_ignoreErrors) { +remoting.HostListApiImpl.defaultResponse_ = function(opt_ignoreErrors) { /** @param {!remoting.Xhr.Response} response */ var result = function(response) { var error = remoting.Error.fromHttpStatus(response.status); if (error.isNone()) { - onDone(); return; } if (opt_ignoreErrors && error.hasTag.apply(error, opt_ignoreErrors)) { - onDone(); return; } - onError(error); + throw error; }; return result; };
diff --git a/remoting/webapp/crd/js/log_to_server.js b/remoting/webapp/crd/js/log_to_server.js index 87cb039e..e76f67a 100644 --- a/remoting/webapp/crd/js/log_to_server.js +++ b/remoting/webapp/crd/js/log_to_server.js
@@ -29,6 +29,8 @@ this.signalStrategy_ = signalStrategy; /** @private {string} */ this.connectionType_ = ''; + /** @private */ + this.authTotalTime_ = 0; this.setSessionId_(); signalStrategy.sendConnectionSetupResults(this); @@ -171,8 +173,10 @@ */ remoting.LogToServer.prototype.log_ = function(entry) { // Log the time taken to get to this point from the time this session started. + // Exclude time taken for authorization. var sessionDurationInSeconds = - (new Date().getTime() - this.sessionStartTime_) / 1000.0; + (new Date().getTime() - this.sessionStartTime_ - + this.authTotalTime_) / 1000.0; entry.addSessionDuration(sessionDurationInSeconds); // Send the stanza to the debug log. @@ -247,3 +251,12 @@ } return idArray.join(''); }; + +/** + * @param {number} totalTime The value of time taken to complete authorization. + * @return {void} Nothing. + */ +remoting.LogToServer.prototype.setAuthTotalTime = function(totalTime) { + this.authTotalTime_ = totalTime; +}; +
diff --git a/remoting/webapp/crd/js/me2me_activity.js b/remoting/webapp/crd/js/me2me_activity.js index 7d2b090..0618464 100644 --- a/remoting/webapp/crd/js/me2me_activity.js +++ b/remoting/webapp/crd/js/me2me_activity.js
@@ -104,8 +104,13 @@ * @param {function(string):void} onPinFetched */ var requestPin = function(supportsPairing, onPinFetched) { + // Set time when PIN was requested. + var authStartTime = new Date().getTime(); that.pinDialog_.show(supportsPairing).then(function(/** string */ pin) { remoting.setMode(remoting.AppMode.CLIENT_CONNECTING); + // Done obtaining PIN information. Log time taken for PIN entry. + var logToServer = that.desktopActivity_.getSession().getLogger(); + logToServer.setAuthTotalTime(new Date().getTime() - authStartTime); onPinFetched(pin); }).catch(function(/** remoting.Error */ error) { base.debug.assert(error.hasTag(remoting.Error.Tag.CANCELLED));
diff --git a/remoting/webapp/crd/js/mock_client_plugin.js b/remoting/webapp/crd/js/mock_client_plugin.js index 698014749..1f2ba5c 100644 --- a/remoting/webapp/crd/js/mock_client_plugin.js +++ b/remoting/webapp/crd/js/mock_client_plugin.js
@@ -7,25 +7,30 @@ * Mock implementation of ClientPlugin for testing. */ -'use strict'; - /** @suppress {duplicate} */ var remoting = remoting || {}; +(function() { + +'use strict'; + /** - * @param {Element} container * @constructor * @implements {remoting.ClientPlugin} */ -remoting.MockClientPlugin = function(container) { - this.container_ = container; +remoting.MockClientPlugin = function() { + /** @private {Element} */ + this.container_ = null; + /** @private */ this.element_ = /** @type {HTMLElement} */ (document.createElement('div')); this.element_.style.backgroundImage = 'linear-gradient(45deg, blue, red)'; - this.container_.appendChild(this.element_); - this.hostDesktop_ = new remoting.MockClientPlugin.HostDesktop(); + /** @private */ + this.hostDesktop_ = + new remoting.ClientPlugin.HostDesktopImpl(this, base.doNothing); + /** @private */ this.extensions_ = new remoting.ProtocolExtensionManager(base.doNothing); - /** @type {remoting.ClientPlugin.ConnectionEventHandler} */ - this.connectionEventHandler = null; + /** @private {remoting.ClientPlugin.ConnectionEventHandler} */ + this.connectionEventHandler_ = null; // Fake initialization result to return. this.mock$initializationResult = true; @@ -35,6 +40,9 @@ remoting.ClientSession.Capability.SEND_INITIAL_RESOLUTION, remoting.ClientSession.Capability.RATE_LIMIT_RESIZE_REQUESTS ]; + + /** @private */ + this.onConnectDeferred_ = new base.Deferred(); }; remoting.MockClientPlugin.prototype.dispose = function() { @@ -65,13 +73,7 @@ remoting.MockClientPlugin.prototype.connect = function(host, localJid, credentialsProvider) { - base.debug.assert(this.connectionEventHandler !== null); - var that = this; - window.requestAnimationFrame(function() { - that.connectionEventHandler.onConnectionStatusUpdate( - remoting.ClientSession.State.CONNECTED, - remoting.ClientSession.ConnectionError.NONE); - }); + this.onConnectDeferred_.resolve(); }; remoting.MockClientPlugin.prototype.injectKeyCombination = function(keys) {}; @@ -125,7 +127,7 @@ remoting.MockClientPlugin.prototype.setConnectionEventHandler = function(handler) { - this.connetionEventHandler = handler; + this.connectionEventHandler_ = handler; }; remoting.MockClientPlugin.prototype.setMouseCursorHandler = @@ -136,60 +138,28 @@ remoting.MockClientPlugin.prototype.setDebugDirtyRegionHandler = function(handler) {}; -/** - * @constructor - * @implements {remoting.HostDesktop} - * @extends {base.EventSourceImpl} - */ -remoting.MockClientPlugin.HostDesktop = function() { - base.inherits(this, base.EventSourceImpl); - /** @private */ - this.width_ = 0; - /** @private */ - this.height_ = 0; - /** @private */ - this.xDpi_ = 96; - /** @private */ - this.yDpi_ = 96; - /** @private */ - this.resizable_ = true; - this.defineEvents(base.values(remoting.HostDesktop.Events)); +/** @param {Element} container */ +remoting.MockClientPlugin.prototype.mock$setContainer = function(container) { + this.container_ = container; + this.container_.appendChild(this.element_); +}; + +/** @return {Promise} */ +remoting.MockClientPlugin.prototype.mock$onConnect = function() { + this.onConnectDeferred_ = new base.Deferred(); + return this.onConnectDeferred_.promise(); }; /** - * @return {{width:number, height:number, xDpi:number, yDpi:number}} - * @override + * @param {remoting.ClientSession.State} status + * @param {remoting.ClientSession.ConnectionError=} opt_error */ -remoting.MockClientPlugin.HostDesktop.prototype.getDimensions = function() { - return { - width: this.width_, - height: this.height_, - xDpi: this.xDpi_, - yDpi: this.yDpi_ - }; -}; - -/** - * @return {boolean} - * @override - */ -remoting.MockClientPlugin.HostDesktop.prototype.isResizable = function() { - return this.resizable_; -}; - -/** - * @param {number} width - * @param {number} height - * @param {number} deviceScale - * @override - */ -remoting.MockClientPlugin.HostDesktop.prototype.resize = - function(width, height, deviceScale) { - this.width_ = width; - this.height_ = height; - this.xDpi_ = this.yDpi_ = Math.floor(deviceScale * 96); - this.raiseEvent(remoting.HostDesktop.Events.sizeChanged, - this.getDimensions()); +remoting.MockClientPlugin.prototype.mock$setConnectionStatus = function( + status, opt_error) { + base.debug.assert(this.connectionEventHandler_ !== null); + var PluginError = remoting.ClientSession.ConnectionError; + var error = opt_error ? opt_error : PluginError.NONE; + this.connectionEventHandler_.onConnectionStatusUpdate(status, error); }; /** @@ -197,14 +167,72 @@ * @implements {remoting.ClientPluginFactory} */ remoting.MockClientPluginFactory = function() { - /** @private {remoting.MockClientPlugin} */ - this.plugin_ = null; + /** @private */ + this.plugin_ = new remoting.MockClientPlugin(); }; remoting.MockClientPluginFactory.prototype.createPlugin = - function(container, onExtensionMessage) { - this.plugin_ = new remoting.MockClientPlugin(container); + function(container, capabilities) { + this.plugin_.mock$setContainer(container); + this.plugin_.mock$capabilities = capabilities; + return this.plugin_; +}; + +/** @return {remoting.MockClientPlugin} */ +remoting.MockClientPluginFactory.prototype.plugin = function() { return this.plugin_; }; remoting.MockClientPluginFactory.prototype.preloadPlugin = function() {}; + +/** + * A class that sets up all the dependencies required for mocking a connection. + * + * @constructor + */ +remoting.MockConnection = function() { + /** @private */ + this.originalPluginFactory_ = remoting.ClientPlugin.factory; + + /** @private */ + this.pluginFactory_ = new remoting.MockClientPluginFactory(); + remoting.ClientPlugin.factory = this.pluginFactory_; + + /** @private */ + this.mockSignalStrategy_ = new remoting.MockSignalStrategy( + 'fake_jid', remoting.SignalStrategy.Type.XMPP); + + /** @private {sinon.TestStub} */ + this.createSignalStrategyStub_ = + sinon.stub(remoting.SignalStrategy, 'create'); + this.createSignalStrategyStub_.returns(this.mockSignalStrategy_); + + /** @private */ + this.originalIdentity_ = remoting.identity; + remoting.identity = new remoting.Identity(); + var identityStub = sinon.stub(remoting.identity, 'getUserInfo'); + identityStub.returns(Promise.resolve({email: 'email', userName: 'userName'})); + + /** @private */ + this.originalSettings_ = remoting.settings; + remoting.settings = new remoting.Settings(); +}; + +/** @return {remoting.MockClientPlugin} */ +remoting.MockConnection.prototype.plugin = function() { + return this.pluginFactory_.plugin(); +}; + +/** @return {remoting.MockSignalStrategy} */ +remoting.MockConnection.prototype.signalStrategy = function() { + return this.mockSignalStrategy_; +}; + +remoting.MockConnection.prototype.restore = function() { + remoting.settings = this.originalSettings_; + remoting.identity = this.originalIdentity_; + remoting.ClientPlugin.factory = this.originalPluginFactory_; + this.createSignalStrategyStub_.restore(); +}; + +})(); \ No newline at end of file
diff --git a/remoting/webapp/crd/js/mock_host_list_api.js b/remoting/webapp/crd/js/mock_host_list_api.js index 7d1516c0..b21462db 100644 --- a/remoting/webapp/crd/js/mock_host_list_api.js +++ b/remoting/webapp/crd/js/mock_host_list_api.js
@@ -58,64 +58,58 @@ } }; -/** - * @param {function(Array<remoting.Host>):void} onDone - * @param {function(!remoting.Error):void} onError - */ -remoting.MockHostListApi.prototype.get = function(onDone, onError) { - remoting.mockIdentity.validateTokenAndCall(onDone, onError, [this.hosts]); +/** @override */ +remoting.MockHostListApi.prototype.get = function() { + var that = this; + new Promise(function(resolve, reject) { + remoting.mockIdentity.validateTokenAndCall( + resolve, remoting.Error.handler(reject), [that.hosts]); + }); }; -/** - * @param {string} hostId - * @param {string} hostName - * @param {string} hostPublicKey - * @param {function():void} onDone - * @param {function(!remoting.Error):void} onError - */ +/** @override */ remoting.MockHostListApi.prototype.put = - function(hostId, hostName, hostPublicKey, onDone, onError) { + function(hostId, hostName, hostPublicKey) { /** @type {remoting.MockHostListApi} */ var that = this; - var onTokenValid = function() { - for (var i = 0; i < that.hosts.length; ++i) { - var host = that.hosts[i]; - if (host.hostId == hostId) { - host.hostName = hostName; - host.hostPublicKey = hostPublicKey; - onDone(); - return; + return new Promise(function(resolve, reject) { + var onTokenValid = function() { + for (var i = 0; i < that.hosts.length; ++i) { + var host = that.hosts[i]; + if (host.hostId == hostId) { + host.hostName = hostName; + host.hostPublicKey = hostPublicKey; + resolve(undefined); + return; + } } - } - console.error('PUT request for unknown host: ' + hostId + - ' (' + hostName + ')'); - onError(remoting.Error.unexpected()); - }; - remoting.mockIdentity.validateTokenAndCall(onTokenValid, onError, []); + console.error('PUT request for unknown host: ' + hostId + + ' (' + hostName + ')'); + reject(remoting.Error.unexpected()); + }; + remoting.mockIdentity.validateTokenAndCall(onTokenValid, reject, []); + }); }; -/** - * @param {string} hostId - * @param {function():void} onDone - * @param {function(!remoting.Error):void} onError - */ -remoting.MockHostListApi.prototype.remove = - function(hostId, onDone, onError) { +/** @override */ +remoting.MockHostListApi.prototype.remove = function(hostId) { /** @type {remoting.MockHostListApi} */ var that = this; - var onTokenValid = function() { - for (var i = 0; i < that.hosts.length; ++i) { - var host = that.hosts[i]; - if (host.hostId == hostId) { - that.hosts.splice(i, 1); - onDone(); - return; + return new Promise(function(resolve, reject) { + var onTokenValid = function() { + for (var i = 0; i < that.hosts.length; ++i) { + var host = that.hosts[i]; + if (host.hostId == hostId) { + that.hosts.splice(i, 1); + resolve(undefined); + return; + } } - } - console.error('DELETE request for unknown host: ' + hostId); - onError(remoting.Error.unexpected()); - }; - remoting.mockIdentity.validateTokenAndCall(onTokenValid, onError, []); + console.error('DELETE request for unknown host: ' + hostId); + reject(remoting.Error.unexpected()); + }; + remoting.mockIdentity.validateTokenAndCall(onTokenValid, reject, []); + }); }; /**
diff --git a/remoting/webapp/crd/js/mock_signal_strategy.js b/remoting/webapp/crd/js/mock_signal_strategy.js index 56b41c0..8fee7ad 100644 --- a/remoting/webapp/crd/js/mock_signal_strategy.js +++ b/remoting/webapp/crd/js/mock_signal_strategy.js
@@ -35,6 +35,10 @@ /** @override */ remoting.MockSignalStrategy.prototype.connect = function() { + var that = this; + Promise.resolve().then(function() { + that.setStateForTesting(remoting.SignalStrategy.State.CONNECTED); + }); }; /** @override */ @@ -65,6 +69,11 @@ : function() {}; }; +/** @param {Element} stanza */ +remoting.MockSignalStrategy.prototype.mock$onIncomingStanza = function(stanza) { + this.onIncomingStanzaCallback_(stanza); +}; + /** @return {remoting.SignalStrategy.State} */ remoting.MockSignalStrategy.prototype.getState = function() { return this.state_;
diff --git a/remoting/webapp/crd/js/xhr.js b/remoting/webapp/crd/js/xhr.js index 1366a99..1b7fa34 100644 --- a/remoting/webapp/crd/js/xhr.js +++ b/remoting/webapp/crd/js/xhr.js
@@ -9,11 +9,13 @@ * Note: a mock version of this API exists in mock_xhr.js. */ -'use strict'; - /** @suppress {duplicate} */ var remoting = remoting || {}; +(function() { + +'use strict'; + /** * @constructor * @param {remoting.Xhr.Params} params @@ -78,54 +80,6 @@ }; /** - * Parameters for the 'start' function. Unless otherwise noted, all - * parameters are optional. - * - * method: (required) The HTTP method to use. - * - * url: (required) The URL to request. - * - * urlParams: Parameters to be appended to the URL. Null-valued - * parameters are omitted. - * - * textContent: Text to be sent as the request body. - * - * formContent: Data to be URL-encoded and sent as the request body. - * Causes Content-type header to be set appropriately. - * - * jsonContent: Data to be JSON-encoded and sent as the request body. - * Causes Content-type header to be set appropriately. - * - * headers: Additional request headers to be sent. Null-valued - * headers are omitted. - * - * withCredentials: Value of the XHR's withCredentials field. - * - * oauthToken: An OAuth2 token used to construct an Authentication - * header. - * - * useIdentity: Use identity API to get an OAuth2 token. - * - * acceptJson: If true, send an Accept header indicating that a JSON - * response is expected. - * - * @typedef {{ - * method: string, - * url:string, - * urlParams:(string|Object<string,?string>|undefined), - * textContent:(string|undefined), - * formContent:(Object|undefined), - * jsonContent:(*|undefined), - * headers:(Object<string,?string>|undefined), - * withCredentials:(boolean|undefined), - * oauthToken:(string|undefined), - * useIdentity:(boolean|undefined), - * acceptJson:(boolean|undefined) - * }} - */ -remoting.Xhr.Params; - -/** * Starts and HTTP request and gets a promise that is resolved when * the request completes. * @@ -160,11 +114,37 @@ }; /** + * The set of possible fields in remoting.Xhr.Params. + * @const + */ +var ALLOWED_PARAMS = [ + 'method', + 'url', + 'urlParams', + 'textContent', + 'formContent', + 'jsonContent', + 'headers', + 'withCredentials', + 'oauthToken', + 'useIdentity', + 'acceptJson' +]; + +/** * @param {remoting.Xhr.Params} params * @throws {Error} if params are invalid * @private */ remoting.Xhr.checkParams_ = function(params) { + // Provide a sensible error message when the user misspells a + // parameter name, since the compiler won't catch it. + for (var field in params) { + if (ALLOWED_PARAMS.indexOf(field) == -1) { + throw new Error('unknow parameter: ' + field); + } + } + if (params.urlParams) { if (params.url.indexOf('?') != -1) { throw new Error('URL may not contain "?" when urlParams is set'); @@ -363,3 +343,53 @@ } return ''; }; + +})(); + +/** + * Parameters for the 'start' function. Unless otherwise noted, all + * parameters are optional. + * + * method: (required) The HTTP method to use. + * + * url: (required) The URL to request. + * + * urlParams: Parameters to be appended to the URL. Null-valued + * parameters are omitted. + * + * textContent: Text to be sent as the request body. + * + * formContent: Data to be URL-encoded and sent as the request body. + * Causes Content-type header to be set appropriately. + * + * jsonContent: Data to be JSON-encoded and sent as the request body. + * Causes Content-type header to be set appropriately. + * + * headers: Additional request headers to be sent. Null-valued + * headers are omitted. + * + * withCredentials: Value of the XHR's withCredentials field. + * + * oauthToken: An OAuth2 token used to construct an Authentication + * header. + * + * useIdentity: Use identity API to get an OAuth2 token. + * + * acceptJson: If true, send an Accept header indicating that a JSON + * response is expected. + * + * @typedef {{ + * method: string, + * url:string, + * urlParams:(string|Object<string,?string>|undefined), + * textContent:(string|undefined), + * formContent:(Object|undefined), + * jsonContent:(*|undefined), + * headers:(Object<string,?string>|undefined), + * withCredentials:(boolean|undefined), + * oauthToken:(string|undefined), + * useIdentity:(boolean|undefined), + * acceptJson:(boolean|undefined) + * }} + */ +remoting.Xhr.Params; \ No newline at end of file
diff --git a/remoting/webapp/crd/js/xhr_unittest.js b/remoting/webapp/crd/js/xhr_unittest.js index f5cb772..fc711ad 100644 --- a/remoting/webapp/crd/js/xhr_unittest.js +++ b/remoting/webapp/crd/js/xhr_unittest.js
@@ -172,6 +172,17 @@ }); }); + +QUnit.test('unexpected parameters', function(assert) { + assert.throws(function() { + new remoting.Xhr({ + method: 'POST', + url: 'http://foo.com', + xyzzy: 'not a real parameter' + }); + }); +}); + // // The typical case. //
diff --git a/remoting/webapp/files.gni b/remoting/webapp/files.gni index 8cb49e7..1a006c04 100644 --- a/remoting/webapp/files.gni +++ b/remoting/webapp/files.gni
@@ -72,6 +72,7 @@ "base/js/ipc_unittest.js", "base/js/protocol_extension_manager_unittest.js", "crd/js/apps_v2_migration_unittest.js", + "crd/js/client_session_unittest.js", "crd/js/desktop_viewport_unittest.js", "crd/js/dns_blackhole_checker_unittest.js", "crd/js/error_unittest.js", @@ -154,6 +155,7 @@ # Client JavaScript files. remoting_webapp_js_client_files = [ + "crd/js/activity.js", "crd/js/client_plugin.js", "crd/js/client_plugin_host_desktop_impl.js", "crd/js/client_plugin_impl.js", @@ -290,8 +292,8 @@ "crd/js/crd_event_handlers.js", "crd/js/crd_experimental.js", "crd/js/crd_main.js", - "crd/js/activity.js", "crd/js/desktop_remoting.js", + "crd/js/desktop_remoting_activity.js", "crd/js/it2me_activity.js", "crd/js/me2me_activity.js", ] @@ -326,45 +328,44 @@ # Webapp background.html generation files. # -remoting_webapp_template_background = - "<(DEPTH)/remoting/webapp/crd/html/template_background.html" +remoting_webapp_template_background = "crd/html/template_background.html" # These JS files are specific to the background page and are not part of # the main JS files. remoting_webapp_background_html_js_files = [ - "webapp/base/js/message_window_helper.js", - "webapp/base/js/message_window_manager.js", - "webapp/crd/js/activation_handler.js", - "webapp/crd/js/app_launcher.js", - "webapp/crd/js/background.js", + "base/js/message_window_helper.js", + "base/js/message_window_manager.js", + "crd/js/activation_handler.js", + "crd/js/app_launcher.js", + "crd/js/background.js", ] # All the JavaScript files required by background.html. -remoting_webapp_background_html_all_js_files = [ - "<@(remoting_webapp_background_html_js_files)", - "webapp/base/js/base.js", - "webapp/base/js/ipc.js", - "webapp/crd/js/client_session.js", - "webapp/crd/js/error.js", - "webapp/crd/js/host_installer.js", - "webapp/crd/js/host_session.js", - "webapp/crd/js/identity.js", - "webapp/crd/js/it2me_host_facade.js", - "webapp/crd/js/l10n.js", - "webapp/crd/js/oauth2.js", - "webapp/crd/js/oauth2_api.js", - "webapp/crd/js/oauth2_api_impl.js", - "webapp/crd/js/plugin_settings.js", - "webapp/crd/js/typecheck.js", - "webapp/crd/js/xhr.js", +remoting_webapp_background_html_all_js_files = + remoting_webapp_background_html_js_files +remoting_webapp_background_html_all_js_files += [ + "base/js/base.js", + "base/js/ipc.js", + "crd/js/client_session.js", + "crd/js/error.js", + "crd/js/host_installer.js", + "crd/js/host_session.js", + "crd/js/identity.js", + "crd/js/it2me_host_facade.js", + "crd/js/l10n.js", + "crd/js/oauth2.js", + "crd/js/oauth2_api.js", + "crd/js/oauth2_api_impl.js", + "crd/js/plugin_settings.js", + "crd/js/typecheck.js", + "crd/js/xhr.js", ] # # Webapp wcs_sandbox.html generation files. # -remoting_webapp_template_wcs_sandbox = - "<(DEPTH)/remoting/webapp/base/html/template_wcs_sandbox.html" +remoting_webapp_template_wcs_sandbox = "base/html/template_wcs_sandbox.html" # These JS files are specific to the WCS sandbox page and are not part of # the main JS files. @@ -376,8 +377,9 @@ ] # All the JavaScript files required by wcs_sandbox.html. -remoting_webapp_wcs_sandbox_html_all_js_files = [ - "<@(remoting_webapp_wcs_sandbox_html_js_files)", +remoting_webapp_wcs_sandbox_html_all_js_files = + remoting_webapp_wcs_sandbox_html_js_files +remoting_webapp_wcs_sandbox_html_all_js_files += [ "crd/js/error.js", "crd/js/plugin_settings.js", ] @@ -387,86 +389,76 @@ # remoting_webapp_template_message_window = - "<(DEPTH)/remoting/webapp/base/html/template_message_window.html" + "base/html/template_message_window.html" # These JS files are specific to the message window page and are not part of # the main JS files. -remoting_webapp_message_window_html_js_files = - [ "webapp/base/js/message_window.js" ] +remoting_webapp_message_window_html_js_files = [ "base/js/message_window.js" ] # All the JavaScript files required by message_window.html. -remoting_webapp_message_window_html_all_js_files = [ - "<@(remoting_webapp_message_window_html_js_files)", - "webapp/base/js/base.js", -] +remoting_webapp_message_window_html_all_js_files = + remoting_webapp_message_window_html_js_files + [ "base/js/base.js" ] # # Complete webapp JS and resource files. # # All the JavaScript files that are shared by webapps. -remoting_webapp_shared_js_files = [ - "<@(remoting_webapp_shared_main_html_js_files)", - "<@(remoting_webapp_background_html_js_files)", - "<@(remoting_webapp_message_window_html_js_files)", - "<@(remoting_webapp_wcs_sandbox_html_js_files)", - - # JS files referenced in manifest.json. - "<@(remoting_webapp_js_auth_v1_files)", -] +remoting_webapp_shared_js_files = remoting_webapp_shared_main_html_js_files + + remoting_webapp_background_html_js_files + + remoting_webapp_message_window_html_js_files + + remoting_webapp_wcs_sandbox_html_js_files + + # JS files referenced in manifest.json. + remoting_webapp_js_auth_v1_files # All the JavaScript files required by DesktopRemoting. -remoting_webapp_crd_js_files = [ - "<@(remoting_webapp_shared_js_files)", - "<@(remoting_webapp_crd_main_html_all_js_files)", -] +remoting_webapp_crd_js_files = + remoting_webapp_shared_js_files + remoting_webapp_crd_main_html_all_js_files remoting_webapp_info_files = [ - "resources/chromoting16.webp", - "resources/chromoting48.webp", - "resources/chromoting128.webp", + "../resources/chromoting16.webp", + "../resources/chromoting48.webp", + "../resources/chromoting128.webp", ] # All the resource files required by DesktopRemoting. remoting_webapp_resource_files = [ - "resources/disclosure_arrow_down.webp", - "resources/disclosure_arrow_right.webp", - "resources/drag.webp", - "resources/host_setup_instructions.webp", - "resources/icon_close.webp", - "resources/icon_cross.webp", - "resources/icon_disconnect.webp", - "resources/icon_fullscreen.webp", - "resources/icon_help.webp", - "resources/icon_host.webp", - "resources/icon_maximize_restore.webp", - "resources/icon_minimize.webp", - "resources/icon_options.webp", - "resources/icon_pencil.webp", - "resources/icon_warning.webp", - "resources/infographic_my_computers.webp", - "resources/infographic_remote_assistance.webp", - "resources/plus.webp", - "resources/reload.webp", - "resources/tick.webp", - "webapp/base/html/connection_stats.css", - "webapp/base/html/main.css", - "webapp/base/html/message_window.css", - "webapp/base/resources/open_sans.css", - "webapp/base/resources/open_sans.woff", - "webapp/base/resources/spinner.gif", - "webapp/crd/html/butter_bar.css", - "webapp/crd/html/toolbar.css", - "webapp/crd/html/menu_button.css", - "webapp/crd/html/window_frame.css", - "webapp/crd/resources/scale-to-fit.webp", + "../resources/disclosure_arrow_down.webp", + "../resources/disclosure_arrow_right.webp", + "../resources/drag.webp", + "../resources/host_setup_instructions.webp", + "../resources/icon_close.webp", + "../resources/icon_cross.webp", + "../resources/icon_disconnect.webp", + "../resources/icon_fullscreen.webp", + "../resources/icon_help.webp", + "../resources/icon_host.webp", + "../resources/icon_maximize_restore.webp", + "../resources/icon_minimize.webp", + "../resources/icon_options.webp", + "../resources/icon_pencil.webp", + "../resources/icon_warning.webp", + "../resources/infographic_my_computers.webp", + "../resources/infographic_remote_assistance.webp", + "../resources/plus.webp", + "../resources/reload.webp", + "../resources/tick.webp", + "base/html/connection_stats.css", + "base/html/main.css", + "base/html/message_window.css", + "base/resources/open_sans.css", + "base/resources/open_sans.woff", + "base/resources/spinner.gif", + "crd/html/butter_bar.css", + "crd/html/toolbar.css", + "crd/html/menu_button.css", + "crd/html/window_frame.css", + "crd/resources/scale-to-fit.webp", ] -remoting_webapp_crd_files = [ - "<@(remoting_webapp_info_files)", - "<@(remoting_webapp_crd_js_files)", - "<@(remoting_webapp_resource_files)", -] +remoting_webapp_crd_files = + remoting_webapp_info_files + remoting_webapp_crd_js_files + + remoting_webapp_resource_files # Files that contain localizable strings. desktop_remoting_webapp_localizable_files = [
diff --git a/remoting/webapp/unittests/spy_promise.js b/remoting/webapp/unittests/spy_promise.js index 3429d276..bb76799b 100644 --- a/remoting/webapp/unittests/spy_promise.js +++ b/remoting/webapp/unittests/spy_promise.js
@@ -245,7 +245,7 @@ if (Promise === base.SpyPromise) { throw Error('base.SpyPromise is already active'); } - Promise = base.SpyPromise; + Promise = /** @type {function(new:Promise)} */(base.SpyPromise); }; /** @@ -291,4 +291,4 @@ }); } }; -})(); \ No newline at end of file +})();
diff --git a/sandbox/sandbox_nacl_nonsfi.gyp b/sandbox/sandbox_nacl_nonsfi.gyp index c757ec5..781df423 100644 --- a/sandbox/sandbox_nacl_nonsfi.gyp +++ b/sandbox/sandbox_nacl_nonsfi.gyp
@@ -39,7 +39,6 @@ }, 'dependencies': [ '../base/base_nacl.gyp:base_nacl_nonsfi', - '../native_client/tools.gyp:prep_toolchain', ], }, ],
diff --git a/sandbox/win/src/broker_services.cc b/sandbox/win/src/broker_services.cc index f23d431..01c7cdd 100644 --- a/sandbox/win/src/broker_services.cc +++ b/sandbox/win/src/broker_services.cc
@@ -404,6 +404,12 @@ // Initialize the startup information from the policy. base::win::StartupInformation startup_info; + // The liftime of |mitigations| and |inherit_handle_list| have to be at least + // as long as |startup_info| because |UpdateProcThreadAttribute| requires that + // its |lpValue| parameter persist until |DeleteProcThreadAttributeList| is + // called; StartupInformation's destructor makes such a call. + DWORD64 mitigations; + HANDLE inherit_handle_list[2]; base::string16 desktop = policy_base->GetAlternateDesktop(); if (!desktop.empty()) { startup_info.startup_info()->lpDesktop = @@ -418,7 +424,6 @@ if (app_container) ++attribute_count; - DWORD64 mitigations; size_t mitigations_size; ConvertProcessMitigationsToPolicy(policy->GetProcessMitigations(), &mitigations, &mitigations_size); @@ -427,7 +432,6 @@ HANDLE stdout_handle = policy_base->GetStdoutHandle(); HANDLE stderr_handle = policy_base->GetStderrHandle(); - HANDLE inherit_handle_list[2]; int inherit_handle_count = 0; if (stdout_handle != INVALID_HANDLE_VALUE) inherit_handle_list[inherit_handle_count++] = stdout_handle;
diff --git a/sql/connection.cc b/sql/connection.cc index d94b4d8..ba799a90 100644 --- a/sql/connection.cc +++ b/sql/connection.cc
@@ -340,7 +340,7 @@ preload_size = file_size; scoped_ptr<char[]> buf(new char[page_size]); - for (sqlite3_int64 pos = 0; pos < file_size; pos += page_size) { + for (sqlite3_int64 pos = 0; pos < preload_size; pos += page_size) { rc = file->pMethods->xRead(file, buf.get(), page_size, pos); if (rc != SQLITE_OK) return;
diff --git a/sync/android/java/src/org/chromium/sync/AndroidSyncSettings.java b/sync/android/java/src/org/chromium/sync/AndroidSyncSettings.java index d24366c..3587e76b 100644 --- a/sync/android/java/src/org/chromium/sync/AndroidSyncSettings.java +++ b/sync/android/java/src/org/chromium/sync/AndroidSyncSettings.java
@@ -160,6 +160,7 @@ public void updateAccount(Account account) { synchronized (mLock) { mAccount = account; + updateSyncability(); } if (updateCachedSettings()) { notifyObservers(); @@ -193,8 +194,8 @@ private void setChromeSyncEnabled(boolean value) { synchronized (mLock) { + updateSyncability(); if (value == mChromeSyncEnabled || mAccount == null) return; - ensureSyncable(); mChromeSyncEnabled = value; StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites(); @@ -205,18 +206,22 @@ } /** - * Ensure Chrome is registered with the Android Sync Manager. + * Ensure Chrome is registered with the Android Sync Manager iff signed in. * * This is what causes the "Chrome" option to appear in Settings -> Accounts -> Sync . * This function must be called within a synchronized block. */ - private void ensureSyncable() { - if (mIsSyncable || mAccount == null) return; + private void updateSyncability() { + boolean shouldBeSyncable = mAccount != null; + if (mIsSyncable == shouldBeSyncable) return; - mIsSyncable = true; + mIsSyncable = shouldBeSyncable; StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites(); - mSyncContentResolverDelegate.setIsSyncable(mAccount, mContractAuthority, 1); + // Make account syncable if there is one. + if (shouldBeSyncable) { + mSyncContentResolverDelegate.setIsSyncable(mAccount, mContractAuthority, 1); + } // Disable the syncability of Chrome for all other accounts. Don't use // our cache as we're touching many accounts that aren't signed in, so this saves
diff --git a/sync/android/javatests/src/org/chromium/sync/AndroidSyncSettingsTest.java b/sync/android/javatests/src/org/chromium/sync/AndroidSyncSettingsTest.java index aeba67f..13c43f64 100644 --- a/sync/android/javatests/src/org/chromium/sync/AndroidSyncSettingsTest.java +++ b/sync/android/javatests/src/org/chromium/sync/AndroidSyncSettingsTest.java
@@ -12,6 +12,9 @@ import org.chromium.base.ThreadUtils; import org.chromium.base.test.util.Feature; import org.chromium.sync.AndroidSyncSettings.AndroidSyncSettingsObserver; +import org.chromium.sync.signin.AccountManagerHelper; +import org.chromium.sync.test.util.AccountHolder; +import org.chromium.sync.test.util.MockAccountManager; import org.chromium.sync.test.util.MockSyncContentResolverDelegate; /** @@ -81,16 +84,17 @@ private Account mAccount; private Account mAlternateAccount; private MockSyncSettingsObserver mSyncSettingsObserver; + private MockAccountManager mAccountManager; @Override protected void setUp() throws Exception { mSyncContentResolverDelegate = new CountingMockSyncContentResolverDelegate(); Context context = getInstrumentation().getTargetContext(); + setupTestAccounts(context); + AndroidSyncSettings.overrideForTests(context, mSyncContentResolverDelegate); mAndroid = AndroidSyncSettings.get(context); mAuthority = mAndroid.getContractAuthority(); - mAccount = new Account("account@example.com", "com.google"); - mAlternateAccount = new Account("alternateAccount@example.com", "com.google"); mAndroid.updateAccount(mAccount); mSyncSettingsObserver = new MockSyncSettingsObserver(); @@ -99,6 +103,21 @@ super.setUp(); } + private void setupTestAccounts(Context context) { + mAccountManager = new MockAccountManager(context, context); + AccountManagerHelper.overrideAccountManagerHelperForTests(context, mAccountManager); + mAccount = setupTestAccount("account@example.com"); + mAlternateAccount = setupTestAccount("alternate@example.com"); + } + + private Account setupTestAccount(String accountName) { + Account account = AccountManagerHelper.createAccountFromName(accountName); + AccountHolder.Builder accountHolder = + AccountHolder.create().account(account).password("password").alwaysAccept(true); + mAccountManager.addAccountHolderExplicitly(accountHolder.build()); + return account; + } + private void enableChromeSyncOnUiThread() { ThreadUtils.runOnUiThreadBlocking(new Runnable() { @Override @@ -294,4 +313,39 @@ assertFalse("disableChromeSync shouldn't observers", mSyncSettingsObserver.receivedNotification()); } + + @SmallTest + @Feature({"Sync"}) + public void testIsSyncableOnSigninAndNotOnSignout() throws InterruptedException { + assertTrue(mSyncContentResolverDelegate.getIsSyncable(mAccount, mAuthority) == 1); + mAndroid.updateAccount(null); + assertTrue(mSyncContentResolverDelegate.getIsSyncable(mAccount, mAuthority) == 0); + mAndroid.updateAccount(mAccount); + assertTrue(mSyncContentResolverDelegate.getIsSyncable(mAccount, mAuthority) == 1); + } + + /** + * Regression test for crbug.com/475299. + */ + @SmallTest + @Feature({"Sync"}) + public void testSyncableIsAlwaysSetWhenEnablingSync() throws InterruptedException { + // Setup bad state. + mSyncContentResolverDelegate.setMasterSyncAutomatically(true); + mSyncContentResolverDelegate.waitForLastNotificationCompleted(); + mSyncContentResolverDelegate.setIsSyncable(mAccount, mAuthority, 1); + mSyncContentResolverDelegate.waitForLastNotificationCompleted(); + mSyncContentResolverDelegate.setSyncAutomatically(mAccount, mAuthority, true); + mSyncContentResolverDelegate.waitForLastNotificationCompleted(); + mSyncContentResolverDelegate.setIsSyncable(mAccount, mAuthority, 0); + mSyncContentResolverDelegate.waitForLastNotificationCompleted(); + assertTrue(mSyncContentResolverDelegate.getIsSyncable(mAccount, mAuthority) == 0); + assertTrue(mSyncContentResolverDelegate.getSyncAutomatically(mAccount, mAuthority)); + + // Ensure bug is fixed. + mAndroid.enableChromeSync(); + assertTrue(mSyncContentResolverDelegate.getIsSyncable(mAccount, mAuthority) == 1); + // Should still be enabled. + assertTrue(mSyncContentResolverDelegate.getSyncAutomatically(mAccount, mAuthority)); + } }
diff --git a/sync/api/attachments/attachment_store.cc b/sync/api/attachments/attachment_store.cc index 76378357..9f09de3 100644 --- a/sync/api/attachments/attachment_store.cc +++ b/sync/api/attachments/attachment_store.cc
@@ -33,7 +33,7 @@ void AttachmentStore::Read(const AttachmentIdList& ids, const ReadCallback& callback) { - frontend_->Read(ids, callback); + frontend_->Read(component_, ids, callback); } void AttachmentStore::Write(const AttachmentList& attachments, @@ -46,13 +46,13 @@ frontend_->DropReference(component_, ids, callback); } -void AttachmentStore::ReadMetadata(const AttachmentIdList& ids, - const ReadMetadataCallback& callback) { - frontend_->ReadMetadata(ids, callback); +void AttachmentStore::ReadMetadataById(const AttachmentIdList& ids, + const ReadMetadataCallback& callback) { + frontend_->ReadMetadataById(component_, ids, callback); } -void AttachmentStore::ReadAllMetadata(const ReadMetadataCallback& callback) { - frontend_->ReadAllMetadata(component_, callback); +void AttachmentStore::ReadMetadata(const ReadMetadataCallback& callback) { + frontend_->ReadMetadata(component_, callback); } scoped_ptr<AttachmentStoreForSync> @@ -124,14 +124,19 @@ frontend()->SetReference(sync_component_, ids); } +void AttachmentStoreForSync::SetModelTypeReference( + const AttachmentIdList& ids) { + frontend()->SetReference(component(), ids); +} + void AttachmentStoreForSync::DropSyncReference(const AttachmentIdList& ids) { frontend()->DropReference(sync_component_, ids, base::Bind(&NoOpDropCallback)); } -void AttachmentStoreForSync::ReadSyncMetadata( +void AttachmentStoreForSync::ReadMetadataForSync( const ReadMetadataCallback& callback) { - frontend()->ReadAllMetadata(sync_component_, callback); + frontend()->ReadMetadata(sync_component_, callback); } } // namespace syncer
diff --git a/sync/api/attachments/attachment_store.h b/sync/api/attachments/attachment_store.h index b29a0924..14fd0fc 100644 --- a/sync/api/attachments/attachment_store.h +++ b/sync/api/attachments/attachment_store.h
@@ -110,15 +110,15 @@ // read metadata for all attachments specified in ids. If any of the // metadata entries do not exist or could not be read, |callback|'s Result // will be UNSPECIFIED_ERROR. - void ReadMetadata(const AttachmentIdList& ids, - const ReadMetadataCallback& callback); + void ReadMetadataById(const AttachmentIdList& ids, + const ReadMetadataCallback& callback); // Asynchronously reads metadata for all attachments with |component_| // reference in the store. // // |callback| will be invoked when finished. If any of the metadata entries // could not be read, |callback|'s Result will be UNSPECIFIED_ERROR. - void ReadAllMetadata(const ReadMetadataCallback& callback); + void ReadMetadata(const ReadMetadataCallback& callback); // Given current AttachmentStore (this) creates separate AttachmentStore that // will be used by sync components (AttachmentService). Resulting @@ -152,6 +152,7 @@ Component component); const scoped_refptr<AttachmentStoreFrontend>& frontend() { return frontend_; } + Component component() const { return component_; } private: scoped_refptr<AttachmentStoreFrontend> frontend_; @@ -174,6 +175,11 @@ // Asynchronously adds reference from sync to attachments. void SetSyncReference(const AttachmentIdList& ids); + // Asynchronously adds reference from model type to attachments. + // Needed in GetOrDownloadAttachments when attachment is in local store but + // doesn't have model type reference. + void SetModelTypeReference(const AttachmentIdList& ids); + // Asynchronously drops sync reference from attachments. void DropSyncReference(const AttachmentIdList& ids); @@ -182,7 +188,7 @@ // // |callback| will be invoked when finished. If any of the metadata entries // could not be read, |callback|'s Result will be UNSPECIFIED_ERROR. - void ReadSyncMetadata(const ReadMetadataCallback& callback); + void ReadMetadataForSync(const ReadMetadataCallback& callback); private: friend class AttachmentStore;
diff --git a/sync/api/attachments/attachment_store_backend.h b/sync/api/attachments/attachment_store_backend.h index 73bc0fb..14f1de7d 100644 --- a/sync/api/attachments/attachment_store_backend.h +++ b/sync/api/attachments/attachment_store_backend.h
@@ -34,7 +34,8 @@ const scoped_refptr<base::SequencedTaskRunner>& callback_task_runner); virtual ~AttachmentStoreBackend(); virtual void Init(const AttachmentStore::InitCallback& callback) = 0; - virtual void Read(const AttachmentIdList& ids, + virtual void Read(AttachmentStore::Component component, + const AttachmentIdList& ids, const AttachmentStore::ReadCallback& callback) = 0; virtual void Write(AttachmentStore::Component component, const AttachmentList& attachments, @@ -44,10 +45,11 @@ virtual void DropReference(AttachmentStore::Component component, const AttachmentIdList& ids, const AttachmentStore::DropCallback& callback) = 0; - virtual void ReadMetadata( + virtual void ReadMetadataById( + AttachmentStore::Component component, const AttachmentIdList& ids, const AttachmentStore::ReadMetadataCallback& callback) = 0; - virtual void ReadAllMetadata( + virtual void ReadMetadata( AttachmentStore::Component component, const AttachmentStore::ReadMetadataCallback& callback) = 0;
diff --git a/sync/internal_api/attachments/attachment_service_impl.cc b/sync/internal_api/attachments/attachment_service_impl.cc index fb65172..be5af274 100644 --- a/sync/internal_api/attachments/attachment_service_impl.cc +++ b/sync/internal_api/attachments/attachment_service_impl.cc
@@ -164,6 +164,10 @@ DCHECK(CalledOnValidThread()); scoped_refptr<GetOrDownloadState> state( new GetOrDownloadState(attachment_ids, callback)); + // SetModelTypeReference() makes attachments visible for model type. + // Needed when attachment doesn't have model type reference, but still + // available in local store. + attachment_store_->SetModelTypeReference(attachment_ids); attachment_store_->Read(attachment_ids, base::Bind(&AttachmentServiceImpl::ReadDone, weak_ptr_factory_.GetWeakPtr(), state));
diff --git a/sync/internal_api/attachments/attachment_service_impl_unittest.cc b/sync/internal_api/attachments/attachment_service_impl_unittest.cc index f619b75..0ca4007 100644 --- a/sync/internal_api/attachments/attachment_service_impl_unittest.cc +++ b/sync/internal_api/attachments/attachment_service_impl_unittest.cc
@@ -33,7 +33,8 @@ void Init(const AttachmentStore::InitCallback& callback) override {} - void Read(const AttachmentIdList& ids, + void Read(AttachmentStore::Component component, + const AttachmentIdList& ids, const AttachmentStore::ReadCallback& callback) override { read_ids.push_back(ids); read_callbacks.push_back(callback); @@ -48,7 +49,7 @@ void SetReference(AttachmentStore::Component component, const AttachmentIdList& ids) override { - set_reference_ids.push_back(ids); + set_reference_ids.push_back(std::make_pair(component, ids)); } void DropReference(AttachmentStore::Component component, @@ -58,13 +59,14 @@ drop_ids.push_back(ids); } - void ReadMetadata( + void ReadMetadataById( + AttachmentStore::Component component, const AttachmentIdList& ids, const AttachmentStore::ReadMetadataCallback& callback) override { NOTREACHED(); } - void ReadAllMetadata( + void ReadMetadata( AttachmentStore::Component component, const AttachmentStore::ReadMetadataCallback& callback) override { NOTREACHED(); @@ -116,7 +118,8 @@ std::vector<AttachmentStore::ReadCallback> read_callbacks; std::vector<AttachmentList> write_attachments; std::vector<AttachmentStore::WriteCallback> write_callbacks; - std::vector<AttachmentIdList> set_reference_ids; + std::vector<std::pair<AttachmentStore::Component, AttachmentIdList>> + set_reference_ids; std::vector<AttachmentIdList> drop_ids; private: @@ -339,6 +342,8 @@ AttachmentIdSet local_attachments; local_attachments.insert(attachment_ids[0]); RunLoop(); + EXPECT_EQ(1U, store()->set_reference_ids.size()); + EXPECT_EQ(AttachmentStore::MODEL_TYPE, store()->set_reference_ids[0].first); store()->RespondToRead(local_attachments); RunLoop(); @@ -443,7 +448,8 @@ } attachment_service()->UploadAttachments(attachment_ids); RunLoop(); - EXPECT_FALSE(store()->set_reference_ids.empty()); + ASSERT_EQ(1U, store()->set_reference_ids.size()); + EXPECT_EQ(AttachmentStore::SYNC, store()->set_reference_ids[0].first); for (unsigned i = 0; i < num_attachments; ++i) { RunLoopAndFireTimer(); // See that the service has issued a read for at least one of the
diff --git a/sync/internal_api/attachments/attachment_store_frontend.cc b/sync/internal_api/attachments/attachment_store_frontend.cc index b9d69950..f1d1ffe42 100644 --- a/sync/internal_api/attachments/attachment_store_frontend.cc +++ b/sync/internal_api/attachments/attachment_store_frontend.cc
@@ -47,12 +47,14 @@ } void AttachmentStoreFrontend::Read( + AttachmentStore::Component component, const AttachmentIdList& ids, const AttachmentStore::ReadCallback& callback) { DCHECK(CalledOnValidThread()); backend_task_runner_->PostTask( - FROM_HERE, base::Bind(&AttachmentStoreBackend::Read, - base::Unretained(backend_.get()), ids, callback)); + FROM_HERE, + base::Bind(&AttachmentStoreBackend::Read, + base::Unretained(backend_.get()), component, ids, callback)); } void AttachmentStoreFrontend::Write( @@ -85,22 +87,24 @@ base::Unretained(backend_.get()), component, ids, callback)); } -void AttachmentStoreFrontend::ReadMetadata( +void AttachmentStoreFrontend::ReadMetadataById( + AttachmentStore::Component component, const AttachmentIdList& ids, const AttachmentStore::ReadMetadataCallback& callback) { DCHECK(CalledOnValidThread()); backend_task_runner_->PostTask( - FROM_HERE, base::Bind(&AttachmentStoreBackend::ReadMetadata, - base::Unretained(backend_.get()), ids, callback)); + FROM_HERE, + base::Bind(&AttachmentStoreBackend::ReadMetadataById, + base::Unretained(backend_.get()), component, ids, callback)); } -void AttachmentStoreFrontend::ReadAllMetadata( +void AttachmentStoreFrontend::ReadMetadata( AttachmentStore::Component component, const AttachmentStore::ReadMetadataCallback& callback) { DCHECK(CalledOnValidThread()); backend_task_runner_->PostTask( FROM_HERE, - base::Bind(&AttachmentStoreBackend::ReadAllMetadata, + base::Bind(&AttachmentStoreBackend::ReadMetadata, base::Unretained(backend_.get()), component, callback)); }
diff --git a/sync/internal_api/attachments/attachment_store_frontend_unittest.cc b/sync/internal_api/attachments/attachment_store_frontend_unittest.cc index a1e8740..590eee8 100644 --- a/sync/internal_api/attachments/attachment_store_frontend_unittest.cc +++ b/sync/internal_api/attachments/attachment_store_frontend_unittest.cc
@@ -27,8 +27,8 @@ const base::Closure& write_called, const base::Closure& set_reference_called, const base::Closure& drop_reference_called, + const base::Closure& read_metadata_by_id_called, const base::Closure& read_metadata_called, - const base::Closure& read_all_metadata_called, const base::Closure& dtor_called) : AttachmentStoreBackend(nullptr), init_called_(init_called), @@ -36,8 +36,8 @@ write_called_(write_called), set_reference_called_(set_reference_called), drop_reference_called_(drop_reference_called), + read_metadata_by_id_called_(read_metadata_by_id_called), read_metadata_called_(read_metadata_called), - read_all_metadata_called_(read_all_metadata_called), dtor_called_(dtor_called) {} ~MockAttachmentStore() override { dtor_called_.Run(); } @@ -46,7 +46,8 @@ init_called_.Run(); } - void Read(const AttachmentIdList& ids, + void Read(AttachmentStore::Component component, + const AttachmentIdList& ids, const AttachmentStore::ReadCallback& callback) override { read_called_.Run(); } @@ -68,16 +69,17 @@ drop_reference_called_.Run(); } - void ReadMetadata( + void ReadMetadataById( + AttachmentStore::Component component, const AttachmentIdList& ids, const AttachmentStore::ReadMetadataCallback& callback) override { - read_metadata_called_.Run(); + read_metadata_by_id_called_.Run(); } - void ReadAllMetadata( + void ReadMetadata( AttachmentStore::Component component, const AttachmentStore::ReadMetadataCallback& callback) override { - read_all_metadata_called_.Run(); + read_metadata_called_.Run(); } base::Closure init_called_; @@ -85,8 +87,8 @@ base::Closure write_called_; base::Closure set_reference_called_; base::Closure drop_reference_called_; + base::Closure read_metadata_by_id_called_; base::Closure read_metadata_called_; - base::Closure read_all_metadata_called_; base::Closure dtor_called_; }; @@ -100,8 +102,8 @@ write_call_count_(0), add_component_call_count_(0), drop_call_count_(0), + read_metadata_by_id_call_count_(0), read_metadata_call_count_(0), - read_all_metadata_call_count_(0), dtor_call_count_(0) {} void SetUp() override { @@ -116,9 +118,9 @@ base::Unretained(this)), base::Bind(&AttachmentStoreFrontendTest::DropReferenceCalled, base::Unretained(this)), - base::Bind(&AttachmentStoreFrontendTest::ReadMetadataCalled, + base::Bind(&AttachmentStoreFrontendTest::ReadMetadataByIdCalled, base::Unretained(this)), - base::Bind(&AttachmentStoreFrontendTest::ReadAllMetadataCalled, + base::Bind(&AttachmentStoreFrontendTest::ReadMetadataCalled, base::Unretained(this)), base::Bind(&AttachmentStoreFrontendTest::DtorCalled, base::Unretained(this)))); @@ -151,9 +153,9 @@ void DropReferenceCalled() { ++drop_call_count_; } - void ReadMetadataCalled() { ++read_metadata_call_count_; } + void ReadMetadataByIdCalled() { ++read_metadata_by_id_call_count_; } - void ReadAllMetadataCalled() { ++read_all_metadata_call_count_; } + void ReadMetadataCalled() { ++read_metadata_call_count_; } void DtorCalled() { ++dtor_call_count_; } @@ -169,8 +171,8 @@ int write_call_count_; int add_component_call_count_; int drop_call_count_; + int read_metadata_by_id_call_count_; int read_metadata_call_count_; - int read_all_metadata_call_count_; int dtor_call_count_; }; @@ -186,7 +188,8 @@ EXPECT_EQ(init_call_count_, 1); attachment_store_frontend_->Read( - id_list, base::Bind(&AttachmentStoreFrontendTest::ReadDone)); + AttachmentStore::SYNC, id_list, + base::Bind(&AttachmentStoreFrontendTest::ReadDone)); EXPECT_EQ(read_call_count_, 0); RunMessageLoop(); EXPECT_EQ(read_call_count_, 1); @@ -207,19 +210,20 @@ RunMessageLoop(); EXPECT_EQ(drop_call_count_, 1); + attachment_store_frontend_->ReadMetadataById( + AttachmentStore::SYNC, id_list, + base::Bind(&AttachmentStoreFrontendTest::ReadMetadataDone)); + EXPECT_EQ(read_metadata_by_id_call_count_, 0); + RunMessageLoop(); + EXPECT_EQ(read_metadata_by_id_call_count_, 1); + attachment_store_frontend_->ReadMetadata( - id_list, base::Bind(&AttachmentStoreFrontendTest::ReadMetadataDone)); + AttachmentStore::SYNC, + base::Bind(&AttachmentStoreFrontendTest::ReadMetadataDone)); EXPECT_EQ(read_metadata_call_count_, 0); RunMessageLoop(); EXPECT_EQ(read_metadata_call_count_, 1); - attachment_store_frontend_->ReadAllMetadata( - AttachmentStore::SYNC, - base::Bind(&AttachmentStoreFrontendTest::ReadMetadataDone)); - EXPECT_EQ(read_all_metadata_call_count_, 0); - RunMessageLoop(); - EXPECT_EQ(read_all_metadata_call_count_, 1); - // Releasing referehce to AttachmentStoreFrontend should result in // MockAttachmentStore being deleted on backend loop. attachment_store_frontend_ = NULL;
diff --git a/sync/internal_api/attachments/attachment_store_test_template.h b/sync/internal_api/attachments/attachment_store_test_template.h index e8c44192..86ea20e 100644 --- a/sync/internal_api/attachments/attachment_store_test_template.h +++ b/sync/internal_api/attachments/attachment_store_test_template.h
@@ -311,7 +311,7 @@ } // Verify getting metadata for specific attachments. -TYPED_TEST_P(AttachmentStoreTest, ReadMetadata) { +TYPED_TEST_P(AttachmentStoreTest, ReadMetadataById) { Attachment attachment1 = Attachment::Create(this->some_data1); Attachment attachment2 = Attachment::Create(this->some_data2); @@ -326,7 +326,7 @@ AttachmentIdList ids; ids.push_back(attachment1.GetId()); ids.push_back(attachment2.GetId()); - this->store->ReadMetadata(ids, this->read_metadata_callback); + this->store->ReadMetadataById(ids, this->read_metadata_callback); this->ClearAndPumpLoop(); // See that only one entry was read. @@ -340,7 +340,7 @@ EXPECT_EQ(AttachmentStore::SUCCESS, this->result); // Try to read metadata for both attachment1 and attachment2 again. - this->store->ReadMetadata(ids, this->read_metadata_callback); + this->store->ReadMetadataById(ids, this->read_metadata_callback); this->ClearAndPumpLoop(); EXPECT_EQ(AttachmentStore::SUCCESS, this->result); EXPECT_EQ(2U, this->attachment_metadata->size()); @@ -353,11 +353,11 @@ EXPECT_EQ(attachment2.GetId(), iter->GetId()); } -// Verify that ReadAllMetadata/ReadSyncMetadata returns metadata for correct +// Verify that ReadMetadata/ReadMetadataForSync returns metadata for correct // set of attachments. -TYPED_TEST_P(AttachmentStoreTest, ReadAllMetadata) { +TYPED_TEST_P(AttachmentStoreTest, ReadMetadata) { // Try to read all metadata from an empty store. - this->store->ReadAllMetadata(this->read_metadata_callback); + this->store->ReadMetadata(this->read_metadata_callback); this->ClearAndPumpLoop(); EXPECT_EQ(AttachmentStore::SUCCESS, this->result); EXPECT_EQ(0U, this->attachment_metadata->size()); @@ -385,49 +385,54 @@ this->store->Drop(ids, this->drop_callback); this->ClearAndPumpLoop(); - // Calling ReadMetadata for above three attachments should return all of them. + // Calling ReadMetadataById for above three attachments should only return + // attachments with model type reference. ids.clear(); - ids.push_back(attachment_sync.GetId()); + ids.push_back(attachment_mt.GetId()); ids.push_back(attachment_sync.GetId()); ids.push_back(attachment_both.GetId()); - this->store->ReadMetadata(ids, this->read_metadata_callback); + this->store->ReadMetadataById(ids, this->read_metadata_callback); this->ClearAndPumpLoop(); - EXPECT_EQ(AttachmentStore::SUCCESS, this->result); - EXPECT_EQ(3U, this->attachment_metadata->size()); + EXPECT_EQ(AttachmentStore::UNSPECIFIED_ERROR, this->result); + EXPECT_EQ(2U, this->attachment_metadata->size()); + AttachmentIdSet model_type_id_set; + model_type_id_set.insert(attachment_mt.GetId()); + model_type_id_set.insert(attachment_both.GetId()); + EXPECT_THAT(model_type_id_set, + testing::Contains((*this->attachment_metadata)[0].GetId())); + EXPECT_THAT(model_type_id_set, + testing::Contains((*this->attachment_metadata)[1].GetId())); - // Call to ReadAllMetadata() should only return attachments with model type + // Call to ReadMetadata() should only return attachments with model type // reference. - this->store->ReadAllMetadata(this->read_metadata_callback); + this->store->ReadMetadata(this->read_metadata_callback); this->ClearAndPumpLoop(); EXPECT_EQ(AttachmentStore::SUCCESS, this->result); EXPECT_EQ(2U, this->attachment_metadata->size()); // Verify that we get all attachments back (the order is undefined). - AttachmentIdSet id_set; - id_set.insert(attachment_mt.GetId()); - id_set.insert(attachment_both.GetId()); - EXPECT_THAT(id_set, + EXPECT_THAT(model_type_id_set, testing::Contains((*this->attachment_metadata)[0].GetId())); - EXPECT_THAT(id_set, + EXPECT_THAT(model_type_id_set, testing::Contains((*this->attachment_metadata)[1].GetId())); - // Call to ReadSyncMetadata() should only return attachments with sync + // Call to ReadMetadataForSync() should only return attachments with sync // reference. - this->store_for_sync->ReadSyncMetadata(this->read_metadata_callback); + this->store_for_sync->ReadMetadataForSync(this->read_metadata_callback); this->ClearAndPumpLoop(); EXPECT_EQ(AttachmentStore::SUCCESS, this->result); EXPECT_EQ(2U, this->attachment_metadata->size()); - id_set.clear(); - id_set.insert(attachment_sync.GetId()); - id_set.insert(attachment_both.GetId()); - EXPECT_THAT(id_set, + AttachmentIdSet sync_id_set; + sync_id_set.insert(attachment_sync.GetId()); + sync_id_set.insert(attachment_both.GetId()); + EXPECT_THAT(sync_id_set, testing::Contains((*this->attachment_metadata)[0].GetId())); - EXPECT_THAT(id_set, + EXPECT_THAT(sync_id_set, testing::Contains((*this->attachment_metadata)[1].GetId())); } -// Verify that setting/droping references gets reflected in ReadAllMetadata and +// Verify that setting/droping references gets reflected in ReadMetadata and // that attachment is only deleted after last reference is droped. TYPED_TEST_P(AttachmentStoreTest, SetSyncReference_DropSyncReference) { Attachment attachment = Attachment::Create(this->some_data1); @@ -439,22 +444,22 @@ // When writing attachment to store only model type reference should be set. this->store->Write(attachments, this->write_callback); - this->store->ReadAllMetadata(this->read_metadata_callback); + this->store->ReadMetadata(this->read_metadata_callback); this->ClearAndPumpLoop(); EXPECT_EQ(AttachmentStore::SUCCESS, this->result); EXPECT_EQ(1U, this->attachment_metadata->size()); EXPECT_EQ(attachment.GetId(), this->attachment_metadata->begin()->GetId()); - this->store_for_sync->ReadSyncMetadata(this->read_metadata_callback); + this->store_for_sync->ReadMetadataForSync(this->read_metadata_callback); this->ClearAndPumpLoop(); EXPECT_EQ(AttachmentStore::SUCCESS, this->result); EXPECT_EQ(0U, this->attachment_metadata->size()); - // After call to SetSyncReference() ReadSyncMetadata should start returning + // After call to SetSyncReference() ReadMetadataForSync should start returning // attachment. this->store_for_sync->SetSyncReference(ids); - this->store_for_sync->ReadSyncMetadata(this->read_metadata_callback); + this->store_for_sync->ReadMetadataForSync(this->read_metadata_callback); this->ClearAndPumpLoop(); EXPECT_EQ(AttachmentStore::SUCCESS, this->result); EXPECT_EQ(1U, this->attachment_metadata->size()); @@ -469,22 +474,22 @@ this->ClearAndPumpLoop(); EXPECT_EQ(AttachmentStore::SUCCESS, this->result); - this->store->ReadAllMetadata(this->read_metadata_callback); + this->store->ReadMetadata(this->read_metadata_callback); this->ClearAndPumpLoop(); EXPECT_EQ(AttachmentStore::SUCCESS, this->result); EXPECT_EQ(0U, this->attachment_metadata->size()); - this->store_for_sync->ReadSyncMetadata(this->read_metadata_callback); + this->store_for_sync->ReadMetadataForSync(this->read_metadata_callback); this->ClearAndPumpLoop(); EXPECT_EQ(AttachmentStore::SUCCESS, this->result); EXPECT_EQ(1U, this->attachment_metadata->size()); - // ReadMetadata should still return this attachment even though it does not - // have model type reference. - this->store->ReadMetadata(ids, this->read_metadata_callback); + // ReadMetadataById should return UNSPECIFIED_ERROR, attachment doesn't have + // model type reference. + this->store->ReadMetadataById(ids, this->read_metadata_callback); this->ClearAndPumpLoop(); - EXPECT_EQ(AttachmentStore::SUCCESS, this->result); - EXPECT_EQ(1U, this->attachment_metadata->size()); + EXPECT_EQ(AttachmentStore::UNSPECIFIED_ERROR, this->result); + EXPECT_EQ(0U, this->attachment_metadata->size()); // Call Drop() again to ensure it doesn't fail. this->store->Drop(ids, this->drop_callback); @@ -492,18 +497,13 @@ EXPECT_EQ(AttachmentStore::SUCCESS, this->result); // After droping sync reference attachment should be deleted from store. - // ReadMetadata should return UNSPECIFIED_ERROR. + // ReadMetadataForSync should return empty result. this->store_for_sync->DropSyncReference(ids); - this->store_for_sync->ReadSyncMetadata(this->read_metadata_callback); + this->store_for_sync->ReadMetadataForSync(this->read_metadata_callback); this->ClearAndPumpLoop(); EXPECT_EQ(AttachmentStore::SUCCESS, this->result); EXPECT_EQ(0U, this->attachment_metadata->size()); - - this->store->ReadMetadata(ids, this->read_metadata_callback); - this->ClearAndPumpLoop(); - EXPECT_EQ(AttachmentStore::UNSPECIFIED_ERROR, this->result); - EXPECT_EQ(0U, this->attachment_metadata->size()); } REGISTER_TYPED_TEST_CASE_P(AttachmentStoreTest, @@ -513,8 +513,8 @@ Read_OneNotFound, Drop_DropTwoButOnlyOneExists, Drop_DoesNotExist, + ReadMetadataById, ReadMetadata, - ReadAllMetadata, SetSyncReference_DropSyncReference); } // namespace syncer
diff --git a/sync/internal_api/attachments/in_memory_attachment_store.cc b/sync/internal_api/attachments/in_memory_attachment_store.cc index f69f102c..340daae 100644 --- a/sync/internal_api/attachments/in_memory_attachment_store.cc +++ b/sync/internal_api/attachments/in_memory_attachment_store.cc
@@ -39,6 +39,7 @@ } void InMemoryAttachmentStore::Read( + AttachmentStore::Component component, const AttachmentIdList& ids, const AttachmentStore::ReadCallback& callback) { DCHECK(CalledOnValidThread()); @@ -47,9 +48,10 @@ scoped_ptr<AttachmentIdList> unavailable_attachments(new AttachmentIdList); for (const auto& id : ids) { - AttachmentEntryMap::iterator attachment_iter = attachments_.find(id); - if (attachment_iter != attachments_.end()) { - const Attachment& attachment = attachment_iter->second.attachment; + AttachmentEntryMap::iterator iter = attachments_.find(id); + if (iter != attachments_.end() && + iter->second.components.count(component) > 0) { + const Attachment& attachment = iter->second.attachment; result_map->insert(std::make_pair(id, attachment)); } else { unavailable_attachments->push_back(id); @@ -104,7 +106,8 @@ PostCallback(base::Bind(callback, result)); } -void InMemoryAttachmentStore::ReadMetadata( +void InMemoryAttachmentStore::ReadMetadataById( + AttachmentStore::Component component, const AttachmentIdList& ids, const AttachmentStore::ReadMetadataCallback& callback) { DCHECK(CalledOnValidThread()); @@ -113,19 +116,22 @@ new AttachmentMetadataList()); for (const auto& id : ids) { - // TODO(pavely): ReadMetadata should only return attachments with component - // reference similarly to ReadAllMetadata behavior. - AttachmentEntryMap::iterator attachments_iter = attachments_.find(id); - if (attachments_iter != attachments_.end()) { - AppendMetadata(metadata_list.get(), attachments_iter->second.attachment); - } else { + AttachmentEntryMap::iterator iter = attachments_.find(id); + if (iter == attachments_.end()) { result_code = AttachmentStore::UNSPECIFIED_ERROR; + continue; } + DCHECK(iter->second.components.size() > 0); + if (iter->second.components.count(component) == 0) { + result_code = AttachmentStore::UNSPECIFIED_ERROR; + continue; + } + AppendMetadata(metadata_list.get(), iter->second.attachment); } PostCallback(base::Bind(callback, result_code, base::Passed(&metadata_list))); } -void InMemoryAttachmentStore::ReadAllMetadata( +void InMemoryAttachmentStore::ReadMetadata( AttachmentStore::Component component, const AttachmentStore::ReadMetadataCallback& callback) { DCHECK(CalledOnValidThread()); @@ -135,6 +141,7 @@ for (AttachmentEntryMap::const_iterator iter = attachments_.begin(); iter != attachments_.end(); ++iter) { + DCHECK(iter->second.components.size() > 0); if (iter->second.components.count(component) > 0) { AppendMetadata(metadata_list.get(), iter->second.attachment); }
diff --git a/sync/internal_api/attachments/on_disk_attachment_store.cc b/sync/internal_api/attachments/on_disk_attachment_store.cc index 00b4019..a2973234 100644 --- a/sync/internal_api/attachments/on_disk_attachment_store.cc +++ b/sync/internal_api/attachments/on_disk_attachment_store.cc
@@ -132,6 +132,17 @@ return component_removed; } +bool AttachmentHasReferenceFromComponent( + const attachment_store_pb::RecordMetadata& record_metadata, + attachment_store_pb::RecordMetadata::Component proto_component) { + for (const auto& reference_component : record_metadata.component()) { + if (reference_component == proto_component) { + return true; + } + } + return false; +} + } // namespace OnDiskAttachmentStore::OnDiskAttachmentStore( @@ -153,6 +164,7 @@ } void OnDiskAttachmentStore::Read( + AttachmentStore::Component component, const AttachmentIdList& ids, const AttachmentStore::ReadCallback& callback) { DCHECK(CalledOnValidThread()); @@ -164,15 +176,13 @@ if (db_) { result_code = AttachmentStore::SUCCESS; - AttachmentIdList::const_iterator iter = ids.begin(); - const AttachmentIdList::const_iterator end = ids.end(); - for (; iter != end; ++iter) { + for (const auto& id : ids) { scoped_ptr<Attachment> attachment; - attachment = ReadSingleAttachment(*iter); + attachment = ReadSingleAttachment(id, component); if (attachment) { - result_map->insert(std::make_pair(*iter, *attachment)); + result_map->insert(std::make_pair(id, *attachment)); } else { - unavailable_attachments->push_back(*iter); + unavailable_attachments->push_back(id); } } result_code = unavailable_attachments->empty() @@ -262,7 +272,8 @@ PostCallback(base::Bind(callback, result_code)); } -void OnDiskAttachmentStore::ReadMetadata( +void OnDiskAttachmentStore::ReadMetadataById( + AttachmentStore::Component component, const AttachmentIdList& ids, const AttachmentStore::ReadMetadataCallback& callback) { DCHECK(CalledOnValidThread()); @@ -272,21 +283,24 @@ new AttachmentMetadataList()); if (db_) { result_code = AttachmentStore::SUCCESS; - // TODO(pavely): ReadMetadata should only return attachments with component - // reference similarly to ReadAllMetadata behavior. for (const auto& id : ids) { attachment_store_pb::RecordMetadata record_metadata; - if (ReadSingleRecordMetadata(id, &record_metadata)) { - metadata_list->push_back(MakeAttachmentMetadata(id, record_metadata)); - } else { + if (!ReadSingleRecordMetadata(id, &record_metadata)) { result_code = AttachmentStore::UNSPECIFIED_ERROR; + continue; } + if (!AttachmentHasReferenceFromComponent(record_metadata, + ComponentToProto(component))) { + result_code = AttachmentStore::UNSPECIFIED_ERROR; + continue; + } + metadata_list->push_back(MakeAttachmentMetadata(id, record_metadata)); } } PostCallback(base::Bind(callback, result_code, base::Passed(&metadata_list))); } -void OnDiskAttachmentStore::ReadAllMetadata( +void OnDiskAttachmentStore::ReadMetadata( AttachmentStore::Component component, const AttachmentStore::ReadMetadataCallback& callback) { DCHECK(CalledOnValidThread()); @@ -320,15 +334,8 @@ result_code = AttachmentStore::UNSPECIFIED_ERROR; continue; } - // Check if attachment has reference from component. - bool has_reference_from_component = false; - for (const auto& reference_component : record_metadata.component()) { - if (reference_component == proto_component) { - has_reference_from_component = true; - break; - } - } - if (has_reference_from_component) + DCHECK(record_metadata.component_size() > 0); + if (AttachmentHasReferenceFromComponent(record_metadata, proto_component)) metadata_list->push_back(MakeAttachmentMetadata(id, record_metadata)); } @@ -393,12 +400,17 @@ } scoped_ptr<Attachment> OnDiskAttachmentStore::ReadSingleAttachment( - const AttachmentId& attachment_id) { + const AttachmentId& attachment_id, + AttachmentStore::Component component) { scoped_ptr<Attachment> attachment; attachment_store_pb::RecordMetadata record_metadata; if (!ReadSingleRecordMetadata(attachment_id, &record_metadata)) { return attachment.Pass(); } + if (!AttachmentHasReferenceFromComponent(record_metadata, + ComponentToProto(component))) + return attachment.Pass(); + const std::string key = MakeDataKeyFromAttachmentId(attachment_id); std::string data_str; leveldb::Status status = db_->Get( @@ -484,6 +496,7 @@ DVLOG(1) << "RecordMetadata::ParseFromString failed"; return false; } + DCHECK(record_metadata->component_size() > 0); return true; }
diff --git a/sync/internal_api/attachments/on_disk_attachment_store_unittest.cc b/sync/internal_api/attachments/on_disk_attachment_store_unittest.cc index 2f45b208..e145172 100644 --- a/sync/internal_api/attachments/on_disk_attachment_store_unittest.cc +++ b/sync/internal_api/attachments/on_disk_attachment_store_unittest.cc
@@ -442,7 +442,7 @@ // Ensure that attachment store handles the case of having an unexpected // record at the end without crashing. -TEST_F(OnDiskAttachmentStoreSpecificTest, ReadAllMetadataWithUnexpectedRecord) { +TEST_F(OnDiskAttachmentStoreSpecificTest, ReadMetadataWithUnexpectedRecord) { // Write a bogus entry at the end of the database. UpdateRecord("zzz", "foobar"); @@ -455,7 +455,7 @@ // Read all metadata. Should be getting no error and zero entries. AttachmentStore::Result metadata_result = AttachmentStore::UNSPECIFIED_ERROR; scoped_ptr<AttachmentMetadataList> metadata_list; - store_->ReadAllMetadata( + store_->ReadMetadata( base::Bind(&OnDiskAttachmentStoreSpecificTest::CopyResultMetadata, base::Unretained(this), &metadata_result, &metadata_list)); RunLoop(); @@ -479,7 +479,7 @@ base::Unretained(this), &write_result)); // Read all metadata back. We should be getting 3 entries. - store_->ReadAllMetadata( + store_->ReadMetadata( base::Bind(&OnDiskAttachmentStoreSpecificTest::CopyResultMetadata, base::Unretained(this), &metadata_result, &metadata_list)); RunLoop(); @@ -506,7 +506,7 @@ // Read all metadata back. We should be getting a failure and // only 2 entries now. - store_->ReadAllMetadata( + store_->ReadMetadata( base::Bind(&OnDiskAttachmentStoreSpecificTest::CopyResultMetadata, base::Unretained(this), &metadata_result, &metadata_list)); RunLoop();
diff --git a/sync/internal_api/public/attachments/attachment_store_frontend.h b/sync/internal_api/public/attachments/attachment_store_frontend.h index 682cb4c..051920d 100644 --- a/sync/internal_api/public/attachments/attachment_store_frontend.h +++ b/sync/internal_api/public/attachments/attachment_store_frontend.h
@@ -37,7 +37,8 @@ void Init(const AttachmentStore::InitCallback& callback); - void Read(const AttachmentIdList& ids, + void Read(AttachmentStore::Component component, + const AttachmentIdList& ids, const AttachmentStore::ReadCallback& callback); void Write(AttachmentStore::Component component, @@ -49,11 +50,12 @@ const AttachmentIdList& ids, const AttachmentStore::DropCallback& callback); - void ReadMetadata(const AttachmentIdList& ids, - const AttachmentStore::ReadMetadataCallback& callback); + void ReadMetadataById(AttachmentStore::Component component, + const AttachmentIdList& ids, + const AttachmentStore::ReadMetadataCallback& callback); - void ReadAllMetadata(AttachmentStore::Component component, - const AttachmentStore::ReadMetadataCallback& callback); + void ReadMetadata(AttachmentStore::Component component, + const AttachmentStore::ReadMetadataCallback& callback); private: friend class base::RefCounted<AttachmentStoreFrontend>;
diff --git a/sync/internal_api/public/attachments/in_memory_attachment_store.h b/sync/internal_api/public/attachments/in_memory_attachment_store.h index 3e53ab1b..d0c7643 100644 --- a/sync/internal_api/public/attachments/in_memory_attachment_store.h +++ b/sync/internal_api/public/attachments/in_memory_attachment_store.h
@@ -31,7 +31,8 @@ // AttachmentStoreBackend implementation. void Init(const AttachmentStore::InitCallback& callback) override; - void Read(const AttachmentIdList& ids, + void Read(AttachmentStore::Component component, + const AttachmentIdList& ids, const AttachmentStore::ReadCallback& callback) override; void Write(AttachmentStore::Component component, const AttachmentList& attachments, @@ -41,10 +42,11 @@ void DropReference(AttachmentStore::Component component, const AttachmentIdList& ids, const AttachmentStore::DropCallback& callback) override; - void ReadMetadata( + void ReadMetadataById( + AttachmentStore::Component component, const AttachmentIdList& ids, const AttachmentStore::ReadMetadataCallback& callback) override; - void ReadAllMetadata( + void ReadMetadata( AttachmentStore::Component component, const AttachmentStore::ReadMetadataCallback& callback) override;
diff --git a/sync/internal_api/public/attachments/on_disk_attachment_store.h b/sync/internal_api/public/attachments/on_disk_attachment_store.h index 3432853..77159f8 100644 --- a/sync/internal_api/public/attachments/on_disk_attachment_store.h +++ b/sync/internal_api/public/attachments/on_disk_attachment_store.h
@@ -41,7 +41,8 @@ // AttachmentStoreBackend implementation. void Init(const AttachmentStore::InitCallback& callback) override; - void Read(const AttachmentIdList& ids, + void Read(AttachmentStore::Component component, + const AttachmentIdList& ids, const AttachmentStore::ReadCallback& callback) override; void Write(AttachmentStore::Component component, const AttachmentList& attachments, @@ -51,10 +52,11 @@ void DropReference(AttachmentStore::Component component, const AttachmentIdList& ids, const AttachmentStore::DropCallback& callback) override; - void ReadMetadata( + void ReadMetadataById( + AttachmentStore::Component component, const AttachmentIdList& ids, const AttachmentStore::ReadMetadataCallback& callback) override; - void ReadAllMetadata( + void ReadMetadata( AttachmentStore::Component component, const AttachmentStore::ReadMetadataCallback& callback) override; @@ -67,7 +69,8 @@ AttachmentStore::Result OpenOrCreate(const base::FilePath& path); // Reads single attachment from store. Returns nullptr in case of errors. scoped_ptr<Attachment> ReadSingleAttachment( - const AttachmentId& attachment_id); + const AttachmentId& attachment_id, + AttachmentStore::Component component); // Writes single attachment to store. Returns false in case of errors. bool WriteSingleAttachment(const Attachment& attachment, AttachmentStore::Component component);
diff --git a/sync/test/android/javatests/src/org/chromium/sync/test/util/MockSyncContentResolverDelegate.java b/sync/test/android/javatests/src/org/chromium/sync/test/util/MockSyncContentResolverDelegate.java index be2622c4..ea0b003b 100644 --- a/sync/test/android/javatests/src/org/chromium/sync/test/util/MockSyncContentResolverDelegate.java +++ b/sync/test/android/javatests/src/org/chromium/sync/test/util/MockSyncContentResolverDelegate.java
@@ -114,10 +114,6 @@ synchronized (mSyncableMapLock) { switch (syncable) { case 0: - if (mSyncAutomaticallySet.contains(key)) { - mSyncAutomaticallySet.remove(key); - } - mIsSyncableMap.put(key, false); break; case 1: @@ -141,7 +137,7 @@ String key = createKey(account, authority); synchronized (mSyncableMapLock) { if (mIsSyncableMap.containsKey(key)) { - return mIsSyncableMap.containsKey(key) ? 1 : 0; + return mIsSyncableMap.get(key) ? 1 : 0; } else { return -1; }
diff --git a/testing/buildbot/chromium.chromiumos.json b/testing/buildbot/chromium.chromiumos.json index 604cc25..f1cc8d6 100644 --- a/testing/buildbot/chromium.chromiumos.json +++ b/testing/buildbot/chromium.chromiumos.json
@@ -9,6 +9,16 @@ } ] }, + "Linux ChromiumOS GN (dbg)": { + "additional_compile_targets": [ + "net_unittests" + ], + "gtest_tests": [ + { + "test": "base_unittests" + } + ] + }, "Linux ChromiumOS Ozone Tests (1)": { "gtest_tests": [ {
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json index b0903de4..3b3c8fe 100644 --- a/testing/buildbot/chromium.fyi.json +++ b/testing/buildbot/chromium.fyi.json
@@ -4,14 +4,14 @@ { "args": [ "--enable-browser-side-navigation", - "--gtest_filter=-BatteryMonitorImplTest.BatteryManagerDefaultValues:BatteryMonitorImplTest.BatteryManagerResolvePromise:BatteryMonitorImplTest.BatteryManagerWithEventListener:BookmarkletTest.DocumentWrite:BookmarkletTest.NonEmptyResult:BookmarkletTest.Redirect:BookmarkletTest.RedirectVoided:ChildProcessLauncherBrowserTest.ChildSpawnFail:CrossProcessFrameTreeBrowserTest.CreateCrossProcessSubframeProxies:CrossProcessFrameTreeBrowserTest.OriginSetOnCrossProcessNavigations:CrossSiteRedirectorBrowserTest.VerifyCrossSiteRedirectURL:CrossSiteTransferTest.NoLeakOnCrossSiteCancel:CrossSiteTransferTest.ReplaceEntryCrossProcessThenTransfer:CrossSiteTransferTest.ReplaceEntryCrossProcessTwice:CrossSiteTransferTest.ReplaceEntryInProcessThenTranfers:DatabaseTest.ReloadPage:DeviceInertialSensorBrowserTest.LightOneOffInfintyTest:DeviceInertialSensorBrowserTest.LightTest:DeviceInertialSensorBrowserTest.MotionNullTest:DeviceInertialSensorBrowserTest.MotionTest:DeviceInertialSensorBrowserTest.OrientationNullTest:DeviceInertialSensorBrowserTest.OrientationTest:DomSerializerTests.SerializeDocumentWithDownloadedIFrame:DOMStorageBrowserTest.SanityCheck:DOMStorageBrowserTest.SanityCheckIncognito:DownloadContentTest.CancelAtFinalRename:DownloadContentTest.CancelAtRelease:DownloadContentTest.CancelInterruptedDownload:DownloadContentTest.CancelResumingDownload:DownloadContentTest.DownloadCancelled:DownloadContentTest.DownloadGZipWithNoContent:DownloadContentTest.DownloadOctetStream:DownloadContentTest.MultiDownload:DownloadContentTest.RemoveDownload:DownloadContentTest.RemoveResumingDownload:DownloadContentTest.ResumeInterruptedDownload:DownloadContentTest.ResumeInterruptedDownloadBadPrecondition:DownloadContentTest.ResumeInterruptedDownloadNoRange:DownloadContentTest.ResumeInterruptedDownloadNoVerifiers:DownloadContentTest.ResumeWithDeletedFile:DownloadContentTest.ResumeWithFileFinalRenameError:DownloadContentTest.ResumeWithFileInitError:DownloadContentTest.ResumeWithFileIntermediateRenameError:DownloadContentTest.ShutdownAtRelease:DownloadContentTest.ShutdownInProgress:File/MediaTest.VideoBearOpusOgg/0:File/MediaTest.VideoBearOpusWebm/0:File/MediaTest.VideoBearSilentTheora/0:File/MediaTest.VideoBearSilentWebm/0:File/MediaTest.VideoBearTheora/0:File/MediaTest.VideoBearWavAlaw/0:File/MediaTest.VideoBearWavMulaw/0:File/MediaTest.VideoBearWavPcm/0:File/MediaTest.VideoBearWavPcm192kHz/0:File/MediaTest.VideoBearWavPcm3kHz/0:File/MediaTest.VideoBearWebm/0:File/MediaTest.VideoTulipWebm/0:FileSystemBrowserTest.CreateTest:FileSystemBrowserTest.RequestTest:FileSystemBrowserTestWithLowQuota.QuotaTest:FrameTreeBrowserTest.FrameTreeAfterCrash:FrameTreeBrowserTest.SandboxFlagsSetForChildFrames:GinBrowserTest.GinAndGarbageCollection:HostZoomMapImplBrowserTest.GetZoomForView_Host:HostZoomMapImplBrowserTest.GetZoomForView_HostAndScheme:IndexedDBBrowserTest.BlobsCountAgainstQuota:IndexedDBBrowserTest.CallbackAccounting:IndexedDBBrowserTest.CanDeleteWhenOverQuotaTest:IndexedDBBrowserTest.ConnectionsClosedOnTabClose:IndexedDBBrowserTest.CursorPrefetch:IndexedDBBrowserTest.CursorTest:IndexedDBBrowserTest.CursorTestIncognito:IndexedDBBrowserTest.DatabaseTest:IndexedDBBrowserTest.DeleteCompactsBackingStore:IndexedDBBrowserTest.DeleteForOriginDeletesBlobs:IndexedDBBrowserTest.DiskFullOnCommit:IndexedDBBrowserTest.DoesntHangTest:IndexedDBBrowserTest.EmptyBlob:IndexedDBBrowserTest.ForceCloseEventTest:IndexedDBBrowserTest.IndexTest:IndexedDBBrowserTest.KeyPathTest:IndexedDBBrowserTest.KeyTypesTest:IndexedDBBrowserTest.LevelDBLogFileTest:IndexedDBBrowserTest.NullKeyPathPersistence:IndexedDBBrowserTest.ObjectStoreTest:IndexedDBBrowserTest.PRE_NullKeyPathPersistence:IndexedDBBrowserTest.PRE_PRE_VersionChangeCrashResilience:IndexedDBBrowserTest.PRE_VersionChangeCrashResilience:IndexedDBBrowserTest.TransactionGetTest:IndexedDBBrowserTest.TransactionTest:IndexedDBBrowserTest.VersionChangeCrashResilience:IndexedDBBrowserTestSingleProcess.RenderThreadShutdownTest:IndexedDBBrowserTestWithCorruptLevelDB.DestroyTest:IndexedDBBrowserTestWithGCExposed.BlobDidAck:IndexedDBBrowserTestWithGCExposed.BlobDidAckPrefetch:IndexedDBBrowserTestWithGCExposed.DatabaseCallbacksTest:IndexedDBBrowserTestWithLowQuota.QuotaTest:IndexedDBBrowserTestWithMissingSSTFile.DestroyTest:IndexedDBBrowserTestWithVersion0Schema.MigrationTest:IndexedDBBrowserTestWithVersion123456Schema.DestroyTest:IndexedDBBrowserTestWithVersion987654SSVData.DestroyTest:ManifestBrowserTest.DummyManifest:ManifestBrowserTest.DynamicManifest:ManifestBrowserTest.ParseErrorManifest:ManifestBrowserTest.ParsingErrorsManifest:MediaCanPlayTypeTest.CodecSupportTest_Avc1Variants:MediaCanPlayTypeTest.CodecSupportTest_Avc3Variants:MediaCanPlayTypeTest.CodecSupportTest_AvcLevels:MediaCanPlayTypeTest.CodecSupportTest_HLS:MediaCanPlayTypeTest.CodecSupportTest_mp3:MediaCanPlayTypeTest.CodecSupportTest_mp4:MediaCanPlayTypeTest.CodecSupportTest_Mp4aVariants:MediaCanPlayTypeTest.CodecSupportTest_ogg:MediaCanPlayTypeTest.CodecSupportTest_wav:MediaCanPlayTypeTest.CodecSupportTest_webm:MediaTest.Navigate:NavigationControllerBrowserTest.CorrectLengthWithCurrentItemReplacement:NavigationControllerBrowserTest.CorrectLengthWithNewTabNavigatingFromWebUI:NavigationControllerBrowserTest.DontIgnoreBackAfterNavEntryLimit:NavigationControllerBrowserTest.ErrorPageReplacement:NavigationControllerBrowserTest.NavigationTypeClassification_ClientSideRedirect:NavigationControllerBrowserTest.NavigationTypeClassification_ExistingPage:NavigationControllerBrowserTest.NavigationTypeClassification_InPage:NavigationControllerBrowserTest.NavigationTypeClassification_NewAndAutoSubframe:NavigationControllerBrowserTest.NavigationTypeClassification_NewPage:NavigationControllerBrowserTest.SubframeOnEmptyPage:OutOfProcessPPAPITest.InputEvent:OutOfProcessPPAPITest.MediaStreamAudioTrack:OutOfProcessPPAPITest.MediaStreamVideoTrack:PluginPowerSaverHelperTest.ClearWhitelistOnNavigate:RendererAccessibilityTest.AccessibilityMessagesQueueWhileSwappedOut:RendererAccessibilityTest.DetachAccessibilityObject:RendererAccessibilityTest.EventOnObjectNotInTree:RendererAccessibilityTest.HideAccessibilityObject:RendererAccessibilityTest.SendFullAccessibilityTreeOnReload:RendererAccessibilityTest.ShowAccessibilityObject:RenderFrameHostImplBrowserTest.IsFocused_AtLoad:RenderFrameHostImplBrowserTest.IsFocused_Widget:RenderFrameHostManagerTest.BackForwardNotStale:RenderFrameHostManagerTest.ClickLinkAfter204Error:RenderFrameHostManagerTest.DisownSubframeOpener:RenderFrameHostManagerTest.DontPreemptNavigationWithFrameTreeUpdate:RenderFrameHostManagerTest.ForceSwapAfterWebUIBindings:RenderFrameHostManagerTest.IgnoreRendererDebugURLsWhenCrashed:RenderFrameHostManagerTest.NoScriptAccessAfterSwapOut:RenderFrameHostManagerTest.RendererDebugURLsDontSwap:RenderFrameHostManagerTest.RestoreFileAccessForHistoryNavigation:RenderFrameHostManagerTest.RestoreSubframeFileAccessForHistoryNavigation:RenderFrameHostManagerTest.ShowLoadingURLUntilSpoof:RenderProcessHostTest.AllProcessExitedCallsBeforeAnyHostDestroyedCalls:RenderViewBrowserTest.ConfirmCacheInformationPlumbed:RenderViewImplTest.DecideNavigationPolicy:RenderViewImplTest.InsertCharacters:RenderViewImplTest.NavigateFrame:RenderViewImplTest.OnHandleKeyboardEvent:RenderViewImplTest.OnNavigationHttpPost:RenderViewImplTest.ReloadWhileSwappedOut:RenderViewImplTest.StaleNavigationsIgnored:RenderViewImplTest.TestBackForward:ResourceDispatcherHostBrowserTest.CrossSiteAfterCrash:ResourceDispatcherHostBrowserTest.CrossSiteFailedRequest:ResourceDispatcherHostBrowserTest.CrossSiteNavigationErrorPage2:ResourceDispatcherHostBrowserTest.CrossSiteNoUnloadOn204:ResourceFetcherTests.ResourceFetcher404:ResourceFetcherTests.ResourceFetcherDeletedInCallback:ResourceFetcherTests.ResourceFetcherDidFail:ResourceFetcherTests.ResourceFetcherDownload:ResourceFetcherTests.ResourceFetcherPost:ResourceFetcherTests.ResourceFetcherSetHeader:ResourceFetcherTests.ResourceFetcherTimeout:SecurityExploitBrowserTest.AttemptDuplicateRenderViewHost:SecurityExploitBrowserTest.AttemptDuplicateRenderWidgetHost:SecurityExploitBrowserTest.InterstitialCommandFromUnderlyingContent:ServiceWorkerBrowserTest.CrossSiteTransfer:ServiceWorkerBrowserTest.ImportsBustMemcache:ServiceWorkerBrowserTest.Reload:ServiceWorkerBrowserTest.ResponseFromHTTPServiceWorkerIsNotMarkedAsSecure:ServiceWorkerBrowserTest.ResponseFromHTTPSServiceWorkerIsMarkedAsSecure:SessionHistoryTest.BasicBackForward:SessionHistoryTest.CrossFrameFormBackForward:SessionHistoryTest.FragmentBackForward:SessionHistoryTest.FrameBackForward:SessionHistoryTest.FrameFormBackForward:SessionHistoryTest.HistoryLength:SessionHistoryTest.JavascriptHistory:SessionHistoryTest.LocationChangeInSubframe:SessionHistoryTest.LocationReplace:SitePerProcessAccessibilityBrowserTest.CrossSiteIframeAccessibility:SitePerProcessBrowserTest.CleanupCrossSiteIframe:SitePerProcessBrowserTest.CompositorFrameSwapped:SitePerProcessBrowserTest.CreateProxiesForNewFrames:SitePerProcessBrowserTest.CrossSiteDidStopLoading:SitePerProcessBrowserTest.CrossSiteIframe:SitePerProcessBrowserTest.DynamicSandboxFlags:SitePerProcessBrowserTest.DynamicSandboxFlagsRemoteToLocal:SitePerProcessBrowserTest.KillingRendererClearsDescendantProxies:SitePerProcessBrowserTest.NavigateRemoteFrame:SitePerProcessBrowserTest.NavigateRemoteFrameToBlankAndDataURLs:SitePerProcessBrowserTest.NavigateRemoteFrameToKilledProcess:SitePerProcessBrowserTest.NavigateRemoteFrameToKilledProcessWithSubtree:SitePerProcessBrowserTest.NavigateWithSiblingRemoteFrame:SitePerProcessBrowserTest.OriginReplication:SitePerProcessBrowserTest.ProxyCreationSkipsSubtree:SitePerProcessBrowserTest.RestrictFrameDetach:SitePerProcessBrowserTest.SandboxFlagsReplication:SitePerProcessBrowserTest.SubframePostMessage:SitePerProcessDevToolsBrowserTest.CrossSiteIframeAgentHost:SpeechRecognitionBrowserTest.OneShotRecognition:StatsTableBrowserTest.StartWithStatTable:TracingControllerTest.EnableAndDisableRecording:TracingControllerTest.EnableAndDisableRecordingWithEmptyFileAndNullCallback:TracingControllerTest.EnableAndDisableRecordingWithFilePath:TracingControllerTest.EnableCaptureAndDisableMonitoring:TracingControllerTest.EnableCaptureAndDisableMonitoringWithEmptyFileAndNullCallback:TracingControllerTest.EnableCaptureAndDisableMonitoringWithFilePath:TracingControllerTest.GetCategories:TransitionBrowserTest.NormalNavigationNotDeferred:TransitionBrowserTest.TransitionNavigationDataIsCleared:TransitionBrowserTest.TransitionNavigationIsDeferred:WebContentsImplBrowserTest.ClearNonVisiblePendingOnFail:WebContentsImplBrowserTest.GetSizeForNewRenderView:WebContentsViewAuraTest.ScreenshotForSwappedOutRenderViews:WebRtcAecDumpBrowserTest.CallWithAecDump:WebRtcAecDumpBrowserTest.CallWithAecDumpEnabledThenDisabled:WebRtcAecDumpBrowserTest.TwoCallsWithAecDump:WebUIMojoTest.EndToEndPing:WorkerTest.IncognitoSharedWorkers:WorkerTest.MultipleSharedWorkers:WorkerTest.MultipleWorkers:WorkerTest.PassMessagePortToSharedWorker:WorkerTest.PassMessagePortToSharedWorkerDontWaitForConnect:WorkerTest.SharedWorkerTlsClientAuth:WorkerTest.SingleSharedWorker:WorkerTest.SingleWorker:WorkerTest.WorkerTlsClientAuth" + "--gtest_filter=-BatteryMonitorImplTest.BatteryManagerDefaultValues:BatteryMonitorImplTest.BatteryManagerResolvePromise:BatteryMonitorImplTest.BatteryManagerWithEventListener:BookmarkletTest.DocumentWrite:BookmarkletTest.NonEmptyResult:BookmarkletTest.Redirect:BookmarkletTest.RedirectVoided:ChildProcessLauncherBrowserTest.ChildSpawnFail:CrossProcessFrameTreeBrowserTest.CreateCrossProcessSubframeProxies:CrossProcessFrameTreeBrowserTest.OriginSetOnCrossProcessNavigations:CrossSiteRedirectorBrowserTest.VerifyCrossSiteRedirectURL:CrossSiteTransferTest.NoLeakOnCrossSiteCancel:CrossSiteTransferTest.ReplaceEntryCrossProcessThenTransfer:CrossSiteTransferTest.ReplaceEntryCrossProcessTwice:CrossSiteTransferTest.ReplaceEntryInProcessThenTranfers:DatabaseTest.ReloadPage:DeviceInertialSensorBrowserTest.LightOneOffInfintyTest:DeviceInertialSensorBrowserTest.LightTest:DeviceInertialSensorBrowserTest.MotionNullTest:DeviceInertialSensorBrowserTest.MotionTest:DeviceInertialSensorBrowserTest.OrientationNullTest:DeviceInertialSensorBrowserTest.OrientationTest:DOMStorageBrowserTest.SanityCheck:DOMStorageBrowserTest.SanityCheckIncognito:DownloadContentTest.CancelAtFinalRename:DownloadContentTest.CancelAtRelease:DownloadContentTest.CancelInterruptedDownload:DownloadContentTest.CancelResumingDownload:DownloadContentTest.DownloadCancelled:DownloadContentTest.DownloadGZipWithNoContent:DownloadContentTest.DownloadOctetStream:DownloadContentTest.MultiDownload:DownloadContentTest.RemoveDownload:DownloadContentTest.RemoveResumingDownload:DownloadContentTest.ResumeInterruptedDownload:DownloadContentTest.ResumeInterruptedDownloadBadPrecondition:DownloadContentTest.ResumeInterruptedDownloadNoRange:DownloadContentTest.ResumeInterruptedDownloadNoVerifiers:DownloadContentTest.ResumeWithDeletedFile:DownloadContentTest.ResumeWithFileFinalRenameError:DownloadContentTest.ResumeWithFileInitError:DownloadContentTest.ResumeWithFileIntermediateRenameError:DownloadContentTest.ShutdownAtRelease:DownloadContentTest.ShutdownInProgress:File/MediaTest.VideoBearOpusOgg/0:File/MediaTest.VideoBearOpusWebm/0:File/MediaTest.VideoBearSilentTheora/0:File/MediaTest.VideoBearSilentWebm/0:File/MediaTest.VideoBearTheora/0:File/MediaTest.VideoBearWavAlaw/0:File/MediaTest.VideoBearWavMulaw/0:File/MediaTest.VideoBearWavPcm/0:File/MediaTest.VideoBearWavPcm192kHz/0:File/MediaTest.VideoBearWavPcm3kHz/0:File/MediaTest.VideoBearWebm/0:File/MediaTest.VideoTulipWebm/0:FileSystemBrowserTest.CreateTest:FileSystemBrowserTest.RequestTest:FileSystemBrowserTestWithLowQuota.QuotaTest:FrameTreeBrowserTest.FrameTreeAfterCrash:FrameTreeBrowserTest.SandboxFlagsSetForChildFrames:IndexedDBBrowserTest.BlobsCountAgainstQuota:IndexedDBBrowserTest.CallbackAccounting:IndexedDBBrowserTest.CanDeleteWhenOverQuotaTest:IndexedDBBrowserTest.ConnectionsClosedOnTabClose:IndexedDBBrowserTest.CursorPrefetch:IndexedDBBrowserTest.CursorTest:IndexedDBBrowserTest.CursorTestIncognito:IndexedDBBrowserTest.DatabaseTest:IndexedDBBrowserTest.DeleteCompactsBackingStore:IndexedDBBrowserTest.DeleteForOriginDeletesBlobs:IndexedDBBrowserTest.DiskFullOnCommit:IndexedDBBrowserTest.DoesntHangTest:IndexedDBBrowserTest.EmptyBlob:IndexedDBBrowserTest.ForceCloseEventTest:IndexedDBBrowserTest.IndexTest:IndexedDBBrowserTest.KeyPathTest:IndexedDBBrowserTest.KeyTypesTest:IndexedDBBrowserTest.LevelDBLogFileTest:IndexedDBBrowserTest.NullKeyPathPersistence:IndexedDBBrowserTest.ObjectStoreTest:IndexedDBBrowserTest.PRE_NullKeyPathPersistence:IndexedDBBrowserTest.PRE_PRE_VersionChangeCrashResilience:IndexedDBBrowserTest.PRE_VersionChangeCrashResilience:IndexedDBBrowserTest.TransactionGetTest:IndexedDBBrowserTest.TransactionTest:IndexedDBBrowserTest.VersionChangeCrashResilience:IndexedDBBrowserTestSingleProcess.RenderThreadShutdownTest:IndexedDBBrowserTestWithCorruptLevelDB.DestroyTest:IndexedDBBrowserTestWithGCExposed.BlobDidAck:IndexedDBBrowserTestWithGCExposed.BlobDidAckPrefetch:IndexedDBBrowserTestWithGCExposed.DatabaseCallbacksTest:IndexedDBBrowserTestWithLowQuota.QuotaTest:IndexedDBBrowserTestWithMissingSSTFile.DestroyTest:IndexedDBBrowserTestWithVersion0Schema.MigrationTest:IndexedDBBrowserTestWithVersion123456Schema.DestroyTest:IndexedDBBrowserTestWithVersion987654SSVData.DestroyTest:ManifestBrowserTest.DummyManifest:ManifestBrowserTest.DynamicManifest:ManifestBrowserTest.ParseErrorManifest:ManifestBrowserTest.ParsingErrorsManifest:MediaCanPlayTypeTest.CodecSupportTest_Avc1Variants:MediaCanPlayTypeTest.CodecSupportTest_Avc3Variants:MediaCanPlayTypeTest.CodecSupportTest_AvcLevels:MediaCanPlayTypeTest.CodecSupportTest_HLS:MediaCanPlayTypeTest.CodecSupportTest_mp3:MediaCanPlayTypeTest.CodecSupportTest_mp4:MediaCanPlayTypeTest.CodecSupportTest_Mp4aVariants:MediaCanPlayTypeTest.CodecSupportTest_ogg:MediaCanPlayTypeTest.CodecSupportTest_wav:MediaCanPlayTypeTest.CodecSupportTest_webm:MediaTest.Navigate:NavigationControllerBrowserTest.CorrectLengthWithCurrentItemReplacement:NavigationControllerBrowserTest.CorrectLengthWithNewTabNavigatingFromWebUI:NavigationControllerBrowserTest.DontIgnoreBackAfterNavEntryLimit:NavigationControllerBrowserTest.ErrorPageReplacement:NavigationControllerBrowserTest.NavigationTypeClassification_ClientSideRedirect:NavigationControllerBrowserTest.NavigationTypeClassification_ExistingPage:NavigationControllerBrowserTest.NavigationTypeClassification_InPage:NavigationControllerBrowserTest.NavigationTypeClassification_NewAndAutoSubframe:NavigationControllerBrowserTest.NavigationTypeClassification_NewPage:NavigationControllerBrowserTest.SubframeOnEmptyPage:OutOfProcessPPAPITest.InputEvent:OutOfProcessPPAPITest.MediaStreamAudioTrack:OutOfProcessPPAPITest.MediaStreamVideoTrack:RendererAccessibilityTest.AccessibilityMessagesQueueWhileSwappedOut:RenderFrameHostImplBrowserTest.IsFocused_AtLoad:RenderFrameHostImplBrowserTest.IsFocused_Widget:RenderFrameHostManagerTest.BackForwardNotStale:RenderFrameHostManagerTest.ClickLinkAfter204Error:RenderFrameHostManagerTest.DisownSubframeOpener:RenderFrameHostManagerTest.DontPreemptNavigationWithFrameTreeUpdate:RenderFrameHostManagerTest.ForceSwapAfterWebUIBindings:RenderFrameHostManagerTest.IgnoreRendererDebugURLsWhenCrashed:RenderFrameHostManagerTest.NoScriptAccessAfterSwapOut:RenderFrameHostManagerTest.RendererDebugURLsDontSwap:RenderFrameHostManagerTest.RestoreFileAccessForHistoryNavigation:RenderFrameHostManagerTest.RestoreSubframeFileAccessForHistoryNavigation:RenderFrameHostManagerTest.ShowLoadingURLUntilSpoof:RenderViewImplTest.DecideNavigationPolicy:RenderViewImplTest.InsertCharacters:RenderViewImplTest.NavigateFrame:RenderViewImplTest.NavigationStartOverride:RenderViewImplTest.OnHandleKeyboardEvent:RenderViewImplTest.OnNavigationHttpPost:RenderViewImplTest.ReloadWhileSwappedOut:RenderViewImplTest.StaleNavigationsIgnored:RenderViewImplTest.TestBackForward:ResourceDispatcherHostBrowserTest.CrossSiteAfterCrash:ResourceDispatcherHostBrowserTest.CrossSiteNoUnloadOn204:ResourceFetcherTests.ResourceFetcher404:ResourceFetcherTests.ResourceFetcherDeletedInCallback:ResourceFetcherTests.ResourceFetcherDidFail:ResourceFetcherTests.ResourceFetcherDownload:ResourceFetcherTests.ResourceFetcherPost:ResourceFetcherTests.ResourceFetcherSetHeader:ResourceFetcherTests.ResourceFetcherTimeout:SecurityExploitBrowserTest.AttemptDuplicateRenderViewHost:SecurityExploitBrowserTest.AttemptDuplicateRenderWidgetHost:SecurityExploitBrowserTest.InterstitialCommandFromUnderlyingContent:ServiceWorkerBrowserTest.CrossSiteTransfer:ServiceWorkerBrowserTest.ImportsBustMemcache:ServiceWorkerBrowserTest.Reload:ServiceWorkerBrowserTest.ResponseFromHTTPServiceWorkerIsNotMarkedAsSecure:ServiceWorkerBrowserTest.ResponseFromHTTPSServiceWorkerIsMarkedAsSecure:SessionHistoryTest.BasicBackForward:SessionHistoryTest.CrossFrameFormBackForward:SessionHistoryTest.FragmentBackForward:SessionHistoryTest.FrameBackForward:SessionHistoryTest.FrameFormBackForward:SessionHistoryTest.HistoryLength:SessionHistoryTest.JavascriptHistory:SessionHistoryTest.LocationChangeInSubframe:SessionHistoryTest.LocationReplace:SitePerProcessAccessibilityBrowserTest.CrossSiteIframeAccessibility:SitePerProcessBrowserTest.CleanupCrossSiteIframe:SitePerProcessBrowserTest.CompositorFrameSwapped:SitePerProcessBrowserTest.CreateProxiesForNewFrames:SitePerProcessBrowserTest.CrossSiteDidStopLoading:SitePerProcessBrowserTest.CrossSiteIframe:SitePerProcessBrowserTest.DynamicSandboxFlags:SitePerProcessBrowserTest.DynamicSandboxFlagsRemoteToLocal:SitePerProcessBrowserTest.KillingRendererClearsDescendantProxies:SitePerProcessBrowserTest.NavigateRemoteFrame:SitePerProcessBrowserTest.NavigateRemoteFrameToBlankAndDataURLs:SitePerProcessBrowserTest.NavigateRemoteFrameToKilledProcess:SitePerProcessBrowserTest.NavigateRemoteFrameToKilledProcessWithSubtree:SitePerProcessBrowserTest.NavigateWithSiblingRemoteFrame:SitePerProcessBrowserTest.OriginReplication:SitePerProcessBrowserTest.ProxyCreationSkipsSubtree:SitePerProcessBrowserTest.RFPHDestruction:SitePerProcessBrowserTest.RestrictFrameDetach:SitePerProcessBrowserTest.SandboxFlagsReplication:SitePerProcessBrowserTest.SubframePostMessage:SitePerProcessDevToolsBrowserTest.CrossSiteIframeAgentHost:SpeechRecognitionBrowserTest.OneShotRecognition:StatsTableBrowserTest.StartWithStatTable:TransitionBrowserTest.NormalNavigationNotDeferred:TransitionBrowserTest.TransitionNavigationDataIsCleared:TransitionBrowserTest.TransitionNavigationIsDeferred:WebContentsImplBrowserTest.ClearNonVisiblePendingOnFail:WebContentsImplBrowserTest.GetSizeForNewRenderView:WebContentsViewAuraTest.ScreenshotForSwappedOutRenderViews:WebUIMojoTest.EndToEndPing:WorkerTest.IncognitoSharedWorkers:WorkerTest.MultipleSharedWorkers:WorkerTest.MultipleWorkers:WorkerTest.PassMessagePortToSharedWorker:WorkerTest.PassMessagePortToSharedWorkerDontWaitForConnect:WorkerTest.SharedWorkerTlsClientAuth:WorkerTest.SingleSharedWorker:WorkerTest.SingleWorker:WorkerTest.WorkerTlsClientAuth" ], "test": "content_browsertests" }, { "args": [ "--enable-browser-side-navigation", - "--gtest_filter=-NavigationControllerTest.CopyRestoredStateAndNavigate:NavigationControllerTest.LoadURL_AbortDoesntCancelPending:NavigationControllerTest.LoadURL_IgnorePreemptsPending:NavigationControllerTest.ShowRendererURLAfterFailUntilModified:NavigationControllerTest.ShowRendererURLInNewTabUntilModified:OverscrollNavigationOverlayTest.LoadUpdateWithoutNonEmptyPaint:RenderFrameHostManagerTest.CancelPendingProperlyDeletesOrSwaps:RenderFrameHostManagerTest.DetachPendingChild:RenderFrameHostManagerTest.PageDoesBackAndReload:RenderViewHostTest.ResetUnloadOnReload:ResourceDispatcherHostTest.TransferNavigationHtml:ResourceDispatcherHostTest.TransferNavigationText:ResourceDispatcherHostTest.TransferNavigationWithProcessCrash:ResourceDispatcherHostTest.TransferNavigationWithTwoRedirects:ResourceDispatcherHostTest.TransferTwoNavigationsHtml:WebContentsImplTest.CrossSiteNavigationBackPreempted:WebContentsImplTest.CrossSiteNavigationPreempted:WebContentsImplTest.CrossSiteNotPreemptedDuringBeforeUnload:WebContentsImplTest.NoEarlyStop" + "--gtest_filter=-NavigationControllerTest.CopyRestoredStateAndNavigate:NavigationControllerTest.LoadURL_AbortDoesntCancelPending:NavigationControllerTest.LoadURL_IgnorePreemptsPending:NavigationControllerTest.ShowRendererURLAfterFailUntilModified:NavigationControllerTest.ShowRendererURLInNewTabUntilModified:RenderFrameHostManagerTest.CancelPendingProperlyDeletesOrSwaps:RenderFrameHostManagerTest.NavigateWithEarlyClose:RenderFrameHostManagerTest.PageDoesBackAndReload:RenderViewHostTest.ResetUnloadOnReload:ResourceDispatcherHostTest.TransferNavigationHtml:ResourceDispatcherHostTest.TransferNavigationText:ResourceDispatcherHostTest.TransferNavigationWithProcessCrash:ResourceDispatcherHostTest.TransferNavigationWithTwoRedirects:ResourceDispatcherHostTest.TransferTwoNavigationsHtml:WebContentsImplTest.ActiveContentsCountChangeBrowsingInstance:WebContentsImplTest.CrossSiteNavigationBackPreempted:WebContentsImplTest.CrossSiteNavigationPreempted:WebContentsImplTest.CrossSiteNotPreemptedDuringBeforeUnload:WebContentsImplTest.NoEarlyStop:WebContentsImplTest.ShowInterstitialCrashRendererThenGoBack:WebContentsImplTest.ShowInterstitialFromBrowserNewNavigationProceed:WebContentsImplTest.ShowInterstitialThenGoBack" ], "test": "content_unittests" } @@ -26,6 +26,9 @@ "test": "cacheinvalidation_unittests" }, { + "test": "cast_base_unittests" + }, + { "test": "cast_media_unittests" }, {
diff --git a/testing/buildbot/chromium.perf.json b/testing/buildbot/chromium.perf.json index 26feb19..f3ad4ca 100644 --- a/testing/buildbot/chromium.perf.json +++ b/testing/buildbot/chromium.perf.json
@@ -192,7 +192,7 @@ "args": [ "performance_browser_tests", "--test-launcher-print-test-stdio=always", - "--gtest_filter=TabCapturePerformanceTest.*", + "--gtest_filter=TabCapturePerformanceTest.*:CastV2PerformanceTest.*", "--test-launcher-jobs=1", "--enable-gpu" ], @@ -227,7 +227,37 @@ "args": [ "performance_browser_tests", "--test-launcher-print-test-stdio=always", - "--gtest_filter=TabCapturePerformanceTest.*", + "--gtest_filter=TabCapturePerformanceTest.*:CastV2PerformanceTest.*", + "--test-launcher-jobs=1", + "--enable-gpu" + ], + "name": "performance_browser_tests", + "script": "gtest_perf_test.py" + } + ] + }, + "Win 7 ATI GPU Perf": { + "scripts": [ + { + "args": [ + "performance_browser_tests", + "--test-launcher-print-test-stdio=always", + "--gtest_filter=TabCapturePerformanceTest.*:CastV2PerformanceTest.*", + "--test-launcher-jobs=1", + "--enable-gpu" + ], + "name": "performance_browser_tests", + "script": "gtest_perf_test.py" + } + ] + }, + "Win 7 Intel GPU Perf": { + "scripts": [ + { + "args": [ + "performance_browser_tests", + "--test-launcher-print-test-stdio=always", + "--gtest_filter=TabCapturePerformanceTest.*:CastV2PerformanceTest.*", "--test-launcher-jobs=1", "--enable-gpu" ], @@ -242,7 +272,7 @@ "args": [ "performance_browser_tests", "--test-launcher-print-test-stdio=always", - "--gtest_filter=TabCapturePerformanceTest.*", + "--gtest_filter=TabCapturePerformanceTest.*:CastV2PerformanceTest.*", "--test-launcher-jobs=1", "--enable-gpu" ], @@ -262,6 +292,17 @@ ], "name": "angle_perftests", "script": "gtest_perf_test.py" + }, + { + "args": [ + "performance_browser_tests", + "--test-launcher-print-test-stdio=always", + "--gtest_filter=TabCapturePerformanceTest.*:CastV2PerformanceTest.*", + "--test-launcher-jobs=1", + "--enable-gpu" + ], + "name": "performance_browser_tests", + "script": "gtest_perf_test.py" } ] }, @@ -271,7 +312,7 @@ "args": [ "performance_browser_tests", "--test-launcher-print-test-stdio=always", - "--gtest_filter=TabCapturePerformanceTest.*", + "--gtest_filter=TabCapturePerformanceTest.*:CastV2PerformanceTest.*", "--test-launcher-jobs=1", "--enable-gpu" ], @@ -286,7 +327,7 @@ "args": [ "performance_browser_tests", "--test-launcher-print-test-stdio=always", - "--gtest_filter=TabCapturePerformanceTest.*", + "--gtest_filter=TabCapturePerformanceTest.*:CastV2PerformanceTest.*", "--test-launcher-jobs=1", "--enable-gpu" ], @@ -296,6 +337,18 @@ ] }, "Win 8 Perf (2)": { - "scripts": [] + "scripts": [ + { + "args": [ + "performance_browser_tests", + "--test-launcher-print-test-stdio=always", + "--gtest_filter=TabCapturePerformanceTest.*:CastV2PerformanceTest.*", + "--test-launcher-jobs=1", + "--enable-gpu" + ], + "name": "performance_browser_tests", + "script": "gtest_perf_test.py" + } + ] } }
diff --git a/testing/buildbot/chromium.win.json b/testing/buildbot/chromium.win.json index 002e188..493461c 100644 --- a/testing/buildbot/chromium.win.json +++ b/testing/buildbot/chromium.win.json
@@ -211,16 +211,7 @@ "test": "wm_unittests" } ], - "scripts": [ - { - "name": "telemetry_unittests", - "script": "telemetry_unittests.py" - }, - { - "name": "telemetry_perf_unittests", - "script": "telemetry_perf_unittests.py" - } - ] + "scripts": [] }, "Win 7 Tests x64 (1)": { "gtest_tests": [ @@ -476,6 +467,26 @@ } ] }, + "Win x64 GN": { + "additional_compile_targets": [ + "net_unittests" + ], + "gtest_tests": [ + { + "test": "base_unittests" + } + ] + }, + "Win x64 GN (dbg)": { + "additional_compile_targets": [ + "net_unittests" + ], + "gtest_tests": [ + { + "test": "base_unittests" + } + ] + }, "Win7 Tests (1)": { "gtest_tests": [ { @@ -985,7 +996,59 @@ }, "Win8 GN": { "additional_compile_targets": [ - "all" + "accessibility_unittests", + "app_list_unittests", + "app_shell_unittests", + "ash_unittests", + "aura_unittests", + "cacheinvalidation_unittests", + "cast_unittests", + "cc_unittests", + "chrome", + "chrome_elf_unittests", + "chromedriver_unittests", + "components_browsertests", + "components_unittests", + "compositor_unittests", + "content_browsertests", + "content_unittests", + "courgette_unittests", + "crypto_unittests", + "device_unittests", + "events_unittests", + "extensions_browsertests", + "extensions_unittests", + "gcm_unit_tests", + "gfx_unittests", + "google_apis_unittests", + "gpu_unittests", + "interactive_ui_tests", + "ipc_mojo_unittests", + "ipc_tests", + "jingle_unittests", + "keyboard_unittests", + "media_unittests", + "message_center_unittests", + "mojo_common_unittests", + "mojo_public_bindings_unittests", + "mojo_public_environment_unittests", + "mojo_public_system_unittests", + "mojo_public_utility_unittests", + "mojo_system_unittests", + "ppapi_unittests", + "printing_unittests", + "sbox_integration_tests", + "sbox_unittests", + "sbox_validation_tests", + "skia_unittests", + "sql_unittests", + "sync_integration_tests", + "sync_unit_tests", + "ui_base_unittests", + "ui_touch_selection_unittests", + "url_unittests", + "views_unittests", + "wm_unittests" ], "gtest_tests": [ { @@ -995,7 +1058,59 @@ }, "Win8 GN (dbg)": { "additional_compile_targets": [ - "all" + "accessibility_unittests", + "app_list_unittests", + "app_shell_unittests", + "ash_unittests", + "aura_unittests", + "cacheinvalidation_unittests", + "cast_unittests", + "cc_unittests", + "chrome", + "chrome_elf_unittests", + "chromedriver_unittests", + "components_browsertests", + "components_unittests", + "compositor_unittests", + "content_browsertests", + "content_unittests", + "courgette_unittests", + "crypto_unittests", + "device_unittests", + "events_unittests", + "extensions_browsertests", + "extensions_unittests", + "gcm_unit_tests", + "gfx_unittests", + "google_apis_unittests", + "gpu_unittests", + "interactive_ui_tests", + "ipc_mojo_unittests", + "ipc_tests", + "jingle_unittests", + "keyboard_unittests", + "media_unittests", + "message_center_unittests", + "mojo_common_unittests", + "mojo_public_bindings_unittests", + "mojo_public_environment_unittests", + "mojo_public_system_unittests", + "mojo_public_utility_unittests", + "mojo_system_unittests", + "ppapi_unittests", + "printing_unittests", + "sbox_integration_tests", + "sbox_unittests", + "sbox_validation_tests", + "skia_unittests", + "sql_unittests", + "sync_integration_tests", + "sync_unit_tests", + "ui_base_unittests", + "ui_touch_selection_unittests", + "url_unittests", + "views_unittests", + "wm_unittests" ], "gtest_tests": [ {
diff --git a/testing/buildbot/client.v8.json b/testing/buildbot/client.v8.json new file mode 100644 index 0000000..f18c6b92 --- /dev/null +++ b/testing/buildbot/client.v8.json
@@ -0,0 +1,14 @@ +{ + "V8 Android GN (dbg)": { + "additional_compile_targets": [ + "chrome_shell_apk" + ], + "gtest_tests": [] + }, + "V8 Linux GN": { + "additional_compile_targets": [ + "all" + ], + "gtest_tests": [] + } +}
diff --git a/testing/buildbot/tryserver.chromium.win.json b/testing/buildbot/tryserver.chromium.win.json index 60650fa2..29e5e233 100644 --- a/testing/buildbot/tryserver.chromium.win.json +++ b/testing/buildbot/tryserver.chromium.win.json
@@ -1,7 +1,59 @@ { "win8_chromium_gn_dbg": { "additional_compile_targets": [ - "all" + "accessibility_unittests", + "app_list_unittests", + "app_shell_unittests", + "ash_unittests", + "aura_unittests", + "cacheinvalidation_unittests", + "cast_unittests", + "cc_unittests", + "chrome", + "chrome_elf_unittests", + "chromedriver_unittests", + "components_browsertests", + "components_unittests", + "compositor_unittests", + "content_browsertests", + "content_unittests", + "courgette_unittests", + "crypto_unittests", + "device_unittests", + "events_unittests", + "extensions_browsertests", + "extensions_unittests", + "gcm_unit_tests", + "gfx_unittests", + "google_apis_unittests", + "gpu_unittests", + "interactive_ui_tests", + "ipc_mojo_unittests", + "ipc_tests", + "jingle_unittests", + "keyboard_unittests", + "media_unittests", + "message_center_unittests", + "mojo_common_unittests", + "mojo_public_bindings_unittests", + "mojo_public_environment_unittests", + "mojo_public_system_unittests", + "mojo_public_utility_unittests", + "mojo_system_unittests", + "ppapi_unittests", + "printing_unittests", + "sbox_integration_tests", + "sbox_unittests", + "sbox_validation_tests", + "skia_unittests", + "sql_unittests", + "sync_integration_tests", + "sync_unit_tests", + "ui_base_unittests", + "ui_touch_selection_unittests", + "url_unittests", + "views_unittests", + "wm_unittests" ], "gtest_tests": [ { @@ -11,7 +63,59 @@ }, "win8_chromium_gn_rel": { "additional_compile_targets": [ - "all" + "accessibility_unittests", + "app_list_unittests", + "app_shell_unittests", + "ash_unittests", + "aura_unittests", + "cacheinvalidation_unittests", + "cast_unittests", + "cc_unittests", + "chrome", + "chrome_elf_unittests", + "chromedriver_unittests", + "components_browsertests", + "components_unittests", + "compositor_unittests", + "content_browsertests", + "content_unittests", + "courgette_unittests", + "crypto_unittests", + "device_unittests", + "events_unittests", + "extensions_browsertests", + "extensions_unittests", + "gcm_unit_tests", + "gfx_unittests", + "google_apis_unittests", + "gpu_unittests", + "interactive_ui_tests", + "ipc_mojo_unittests", + "ipc_tests", + "jingle_unittests", + "keyboard_unittests", + "media_unittests", + "message_center_unittests", + "mojo_common_unittests", + "mojo_public_bindings_unittests", + "mojo_public_environment_unittests", + "mojo_public_system_unittests", + "mojo_public_utility_unittests", + "mojo_system_unittests", + "ppapi_unittests", + "printing_unittests", + "sbox_integration_tests", + "sbox_unittests", + "sbox_validation_tests", + "skia_unittests", + "sql_unittests", + "sync_integration_tests", + "sync_unit_tests", + "ui_base_unittests", + "ui_touch_selection_unittests", + "url_unittests", + "views_unittests", + "wm_unittests" ], "gtest_tests": [ {
diff --git a/testing/buildbot/tryserver.v8.json b/testing/buildbot/tryserver.v8.json new file mode 100644 index 0000000..aa8e50d --- /dev/null +++ b/testing/buildbot/tryserver.v8.json
@@ -0,0 +1,14 @@ +{ + "v8_android_chromium_gn_dbg": { + "additional_compile_targets": [ + "chrome_shell_apk" + ], + "gtest_tests": [] + }, + "v8_linux_chromium_gn_rel": { + "additional_compile_targets": [ + "all" + ], + "gtest_tests": [] + } +}
diff --git a/testing/chromoting/browser_tests_launcher.py b/testing/chromoting/browser_tests_launcher.py index 053ea5e..2fceb7c 100644 --- a/testing/chromoting/browser_tests_launcher.py +++ b/testing/chromoting/browser_tests_launcher.py
@@ -50,9 +50,8 @@ results = subprocess.check_output(cmd_line, stderr=subprocess.STDOUT, shell=True) except subprocess.CalledProcessError, e: - raise Exception('Exception %s running command %s\n' % - (e, command)) - else: + results = e.output + finally: print results return results @@ -196,7 +195,7 @@ # Was there any test failure? if TEST_FAILURE: print '++++++++++AT LEAST 1 TEST FAILED++++++++++' - print FAILING_TESTS + print FAILING_TESTS.rstrip('\n') print '++++++++++++++++++++++++++++++++++++++++++' raise Exception('At least one test failed.')
diff --git a/testing/iossim/OWNERS b/testing/iossim/OWNERS index 7f8c2f83..1b3348e7 100644 --- a/testing/iossim/OWNERS +++ b/testing/iossim/OWNERS
@@ -1,3 +1,2 @@ -lliabraa@chromium.org rohitrao@chromium.org stuartmorgan@chromium.org
diff --git a/third_party/boringssl/boringssl_nacl.gyp b/third_party/boringssl/boringssl_nacl.gyp index 6ab4b9d..cfd43b9 100644 --- a/third_party/boringssl/boringssl_nacl.gyp +++ b/third_party/boringssl/boringssl_nacl.gyp
@@ -16,7 +16,6 @@ 'build_pnacl_newlib': 1, }, 'dependencies': [ - '<(DEPTH)/native_client/tools.gyp:prep_toolchain', '<(DEPTH)/native_client_sdk/native_client_sdk_untrusted.gyp:nacl_io_untrusted', ], 'includes': [
diff --git a/third_party/closure_compiler/compiler/compiler.jar b/third_party/closure_compiler/compiler/compiler.jar index 61d06bc9..252ac9eb 100644 --- a/third_party/closure_compiler/compiler/compiler.jar +++ b/third_party/closure_compiler/compiler/compiler.jar Binary files differ
diff --git a/third_party/closure_compiler/externs/chrome_extensions.js b/third_party/closure_compiler/externs/chrome_extensions.js index e7f1320a..06198b4c1 100644 --- a/third_party/closure_compiler/externs/chrome_extensions.js +++ b/third_party/closure_compiler/externs/chrome_extensions.js
@@ -7550,29 +7550,33 @@ /** - * @param {string} notificationId - * @param {!chrome.notifications.NotificationOptions} options - * @param {function(string): void} callback + * @param {string|!chrome.notifications.NotificationOptions} + * notificationIdOrOptions + * @param {(!chrome.notifications.NotificationOptions|function(string): void)=} + * opt_optionsOrCallback + * @param {(function(string): void)=} opt_callback * @see http://developer.chrome.com/extensions/notifications.html#method-create */ -chrome.notifications.create = function(notificationId, options, callback) {}; +chrome.notifications.create = function(notificationIdOrOptions, + opt_optionsOrCallback, opt_callback) {}; /** * @param {string} notificationId * @param {!chrome.notifications.NotificationOptions} options - * @param {!chrome.notifications.BooleanCallback} callback + * @param {chrome.notifications.BooleanCallback=} opt_callback * @see http://developer.chrome.com/extensions/notifications.html#method-update */ -chrome.notifications.update = function(notificationId, options, callback) {}; +chrome.notifications.update = + function(notificationId, options, opt_callback) {}; /** * @param {string} notificationId - * @param {!chrome.notifications.BooleanCallback} callback + * @param {!chrome.notifications.BooleanCallback=} opt_callback * @see http://developer.chrome.com/extensions/notifications.html#method-clear */ -chrome.notifications.clear = function(notificationId, callback) {}; +chrome.notifications.clear = function(notificationId, opt_callback) {}; /** @@ -8605,7 +8609,7 @@ /** * @type {number|undefined} */ -chrome.networkingPrivate.WiFiStateProperties.prototype.SignalStrength; +chrome.networkingPrivate.WiMAXStateProperties.prototype.SignalStrength; /**
diff --git a/third_party/closure_compiler/externs/developer_private.js b/third_party/closure_compiler/externs/developer_private.js index 2dbf1db..b098b243 100644 --- a/third_party/closure_compiler/externs/developer_private.js +++ b/third_party/closure_compiler/externs/developer_private.js
@@ -386,6 +386,7 @@ VIEW_REGISTERED: 'VIEW_REGISTERED', VIEW_UNREGISTERED: 'VIEW_UNREGISTERED', ERROR_ADDED: 'ERROR_ADDED', + PREFS_CHANGED: 'PREFS_CHANGED', }; /**
diff --git a/third_party/closure_compiler/runner/runner.jar b/third_party/closure_compiler/runner/runner.jar index 2412698..eadbd1d 100644 --- a/third_party/closure_compiler/runner/runner.jar +++ b/third_party/closure_compiler/runner/runner.jar Binary files differ
diff --git a/third_party/closure_compiler/runner/src/com/google/javascript/jscomp/ChromePass.java b/third_party/closure_compiler/runner/src/com/google/javascript/jscomp/ChromePass.java index 274f0f8..8a4d814 100644 --- a/third_party/closure_compiler/runner/src/com/google/javascript/jscomp/ChromePass.java +++ b/third_party/closure_compiler/runner/src/com/google/javascript/jscomp/ChromePass.java
@@ -155,7 +155,7 @@ private void setJsDocWithType(Node target, Node type) { JSDocInfoBuilder builder = new JSDocInfoBuilder(false); builder.recordType(new JSTypeExpression(type, "")); - target.setJSDocInfo(builder.build(target)); + target.setJSDocInfo(builder.build()); } private boolean visitMakePublic(Node call, Node exprResult) {
diff --git a/third_party/jsoncpp/jsoncpp_nacl.gyp b/third_party/jsoncpp/jsoncpp_nacl.gyp index d60510c..565cb38 100644 --- a/third_party/jsoncpp/jsoncpp_nacl.gyp +++ b/third_party/jsoncpp/jsoncpp_nacl.gyp
@@ -24,9 +24,6 @@ # overrides/src/lib_json/json_value.cpp:38. '-fno-strict-aliasing', ], - 'dependencies': [ - '<(DEPTH)/native_client/tools.gyp:prep_toolchain', - ], }, ], }],
diff --git a/third_party/libevent/libevent_nacl_nonsfi.gyp b/third_party/libevent/libevent_nacl_nonsfi.gyp index 704de105..37a78ad6 100644 --- a/third_party/libevent/libevent_nacl_nonsfi.gyp +++ b/third_party/libevent/libevent_nacl_nonsfi.gyp
@@ -40,9 +40,6 @@ 'build_pnacl_newlib': 0, 'build_nonsfi_helper': 1, }, - 'dependencies': [ - '../../native_client/tools.gyp:prep_toolchain', - ], }, ], }],
diff --git a/third_party/libjingle/BUILD.gn b/third_party/libjingle/BUILD.gn index 99fadfb..f3991238 100644 --- a/third_party/libjingle/BUILD.gn +++ b/third_party/libjingle/BUILD.gn
@@ -99,10 +99,7 @@ defines += [ "ANDROID" ] } if (is_posix) { - defines += [ - "POSIX", - "WEBRTC_POSIX", - ] + defines += [ "WEBRTC_POSIX" ] } # TODO(GYP): Support these in GN.
diff --git a/third_party/libjingle/README.chromium b/third_party/libjingle/README.chromium index 9b8891c..50c79fc 100644 --- a/third_party/libjingle/README.chromium +++ b/third_party/libjingle/README.chromium
@@ -1,7 +1,7 @@ Name: libjingle URL: http://code.google.com/p/webrtc/ Version: unknown -Revision: 9029 +Revision: 9040 License: BSD License File: source/talk/COPYING Security Critical: yes
diff --git a/third_party/libjingle/libjingle.gyp b/third_party/libjingle/libjingle.gyp index 6b72ac2..7cda1966 100644 --- a/third_party/libjingle/libjingle.gyp +++ b/third_party/libjingle/libjingle.gyp
@@ -130,7 +130,6 @@ }], ['os_posix==1', { 'defines': [ - 'POSIX', 'WEBRTC_POSIX', ], }], @@ -241,7 +240,6 @@ }], ['os_posix == 1', { 'defines': [ - 'POSIX', 'WEBRTC_POSIX', ], }],
diff --git a/third_party/libjingle/libjingle_nacl.gyp b/third_party/libjingle/libjingle_nacl.gyp index 6848b29..c7049ec4 100644 --- a/third_party/libjingle/libjingle_nacl.gyp +++ b/third_party/libjingle/libjingle_nacl.gyp
@@ -25,7 +25,6 @@ 'use_openssl': 1, }, 'dependencies': [ - '<(DEPTH)/native_client/tools.gyp:prep_toolchain', '<(DEPTH)/native_client_sdk/native_client_sdk_untrusted.gyp:nacl_io_untrusted', '<(DEPTH)/third_party/expat/expat_nacl.gyp:expat_nacl', '<(DEPTH)/third_party/boringssl/boringssl_nacl.gyp:boringssl_nacl', @@ -38,7 +37,6 @@ 'HAVE_OPENSSL_SSL_H', 'NO_MAIN_THREAD_WRAPPING', 'NO_SOUND_SYSTEM', - 'POSIX', 'WEBRTC_POSIX', 'SRTP_RELATIVE_PATH', 'SSL_USE_OPENSSL', @@ -287,7 +285,6 @@ 'GTEST_RELATIVE_PATH', 'NO_MAIN_THREAD_WRAPPING', 'NO_SOUND_SYSTEM', - 'POSIX', 'WEBRTC_POSIX', 'SRTP_RELATIVE_PATH', 'SSL_USE_OPENSSL',
diff --git a/third_party/liblouis/liblouis_nacl.gyp b/third_party/liblouis/liblouis_nacl.gyp index 081658f..478252af 100644 --- a/third_party/liblouis/liblouis_nacl.gyp +++ b/third_party/liblouis/liblouis_nacl.gyp
@@ -65,9 +65,6 @@ 'src/liblouis/transcommon.ci', 'src/liblouis/wrappers.c', ], - 'dependencies': [ - '../../native_client/tools.gyp:prep_toolchain', - ], }, { 'target_name': 'liblouis_nacl_wrapper_nacl', @@ -119,7 +116,6 @@ ], 'dependencies': [ '../../native_client/src/untrusted/nacl/nacl.gyp:nacl_lib', - '../../native_client/tools.gyp:prep_toolchain', '../../native_client_sdk/native_client_sdk_untrusted.gyp:nacl_io_untrusted', '../../ppapi/native_client/native_client.gyp:ppapi_lib', '../../ppapi/ppapi_nacl.gyp:ppapi_cpp_lib',
diff --git a/third_party/libusb/README.chromium b/third_party/libusb/README.chromium index fc81c05..e74344c 100644 --- a/third_party/libusb/README.chromium +++ b/third_party/libusb/README.chromium
@@ -19,3 +19,4 @@ - upstream-tick147.patch has been applied. - linux-udev.patch has been applied. - composite-hid-close.patch has been applied. +- assign-endpoints-checks.patch has been applied. \ No newline at end of file
diff --git a/third_party/libusb/assign-endpoints-checks.patch b/third_party/libusb/assign-endpoints-checks.patch new file mode 100644 index 0000000..7ed09bdb --- /dev/null +++ b/third_party/libusb/assign-endpoints-checks.patch
@@ -0,0 +1,17 @@ +diff --git a/third_party/libusb/src/libusb/os/windows_usb.c b/third_party/libusb/src/libusb/os/windows_usb.c +index 259897e..4469992 100644 +--- a/third_party/libusb/src/libusb/os/windows_usb.c ++++ b/third_party/libusb/src/libusb/os/windows_usb.c +@@ -663,6 +663,12 @@ static int windows_assign_endpoints(struct libusb_device_handle *dev_handle, int + return r; + } + ++ if (iface >= conf_desc->bNumInterfaces || ++ altsetting >= conf_desc->interface[iface].num_altsetting) { ++ usbi_dbg("interface %d, altsetting %d out of range", iface, altsetting); ++ return LIBUSB_ERROR_INVALID_PARAM; ++ } ++ + if_desc = &conf_desc->interface[iface].altsetting[altsetting]; + safe_free(priv->usb_interface[iface].endpoint); +
diff --git a/third_party/libusb/src/libusb/os/windows_usb.c b/third_party/libusb/src/libusb/os/windows_usb.c index 259897e..4469992 100644 --- a/third_party/libusb/src/libusb/os/windows_usb.c +++ b/third_party/libusb/src/libusb/os/windows_usb.c
@@ -663,6 +663,12 @@ return r; } + if (iface >= conf_desc->bNumInterfaces || + altsetting >= conf_desc->interface[iface].num_altsetting) { + usbi_dbg("interface %d, altsetting %d out of range", iface, altsetting); + return LIBUSB_ERROR_INVALID_PARAM; + } + if_desc = &conf_desc->interface[iface].altsetting[altsetting]; safe_free(priv->usb_interface[iface].endpoint);
diff --git a/third_party/modp_b64/modp_b64_nacl.gyp b/third_party/modp_b64/modp_b64_nacl.gyp index cd0d210d..e2f4a254 100644 --- a/third_party/modp_b64/modp_b64_nacl.gyp +++ b/third_party/modp_b64/modp_b64_nacl.gyp
@@ -16,9 +16,6 @@ 'build_newlib': 1, 'build_pnacl_newlib': 1, }, - 'dependencies': [ - '<(DEPTH)/native_client/tools.gyp:prep_toolchain', - ], 'sources': [ 'modp_b64.cc', 'modp_b64.h',
diff --git a/third_party/mojo/src/mojo/edk/embedder/simple_platform_shared_buffer_win.cc b/third_party/mojo/src/mojo/edk/embedder/simple_platform_shared_buffer_win.cc index 0737465..49b82d86 100644 --- a/third_party/mojo/src/mojo/edk/embedder/simple_platform_shared_buffer_win.cc +++ b/third_party/mojo/src/mojo/edk/embedder/simple_platform_shared_buffer_win.cc
@@ -48,9 +48,8 @@ ScopedPlatformHandle platform_handle) { DCHECK(!handle_.is_valid()); - // TODO(vtl): Implement. - NOTIMPLEMENTED(); - return false; + handle_ = platform_handle.Pass(); + return true; } scoped_ptr<PlatformSharedBufferMapping> SimplePlatformSharedBuffer::MapImpl(
diff --git a/third_party/mojo/src/mojo/edk/system/raw_channel_win.cc b/third_party/mojo/src/mojo/edk/system/raw_channel_win.cc index 4417937..a422c9e 100644 --- a/third_party/mojo/src/mojo/edk/system/raw_channel_win.cc +++ b/third_party/mojo/src/mojo/edk/system/raw_channel_win.cc
@@ -15,6 +15,7 @@ #include "base/macros.h" #include "base/memory/scoped_ptr.h" #include "base/message_loop/message_loop.h" +#include "base/process/process.h" #include "base/synchronization/lock.h" #include "base/win/windows_version.h" #include "mojo/edk/embedder/platform_handle.h" @@ -360,8 +361,7 @@ } size_t RawChannelWin::GetSerializedPlatformHandleSize() const { - // TODO(vtl): Implement. - return 0; + return sizeof(DWORD) + sizeof(HANDLE); } RawChannel::IOResult RawChannelWin::Read(size_t* bytes_read) { @@ -437,9 +437,37 @@ embedder::ScopedPlatformHandleVectorPtr RawChannelWin::GetReadPlatformHandles( size_t num_platform_handles, const void* platform_handle_table) { - // TODO(vtl): Implement. - NOTIMPLEMENTED(); - return embedder::ScopedPlatformHandleVectorPtr(); + // TODO(jam): this code will have to be updated once it's used in a sandbox + // and the receiving process doesn't have duplicate permission for the + // receiver. Once there's a broker and we have a connection to it (possibly + // through ConnectionManager), then we can make a sync IPC to it here to get a + // token for this handle, and it will duplicate the handle to is process. Then + // we pass the token to the receiver, which will then make a sync call to the + // broker to get a duplicated handle. This will also allow us to avoid leaks + // of the handle if the receiver dies, since the broker can notice that. + DCHECK_GT(num_platform_handles, 0u); + embedder::ScopedPlatformHandleVectorPtr rv( + new embedder::PlatformHandleVector()); + + const char* serialization_data = + static_cast<const char*>(platform_handle_table); + for (size_t i = 0; i < num_platform_handles; i++) { + DWORD pid = *reinterpret_cast<const DWORD*>(serialization_data); + serialization_data += sizeof(DWORD); + HANDLE source_handle = *reinterpret_cast<const HANDLE*>(serialization_data); + serialization_data += sizeof(HANDLE); + base::Process sender = + base::Process::OpenWithAccess(pid, PROCESS_DUP_HANDLE); + DCHECK(sender.IsValid()); + HANDLE target_handle = NULL; + BOOL dup_result = + DuplicateHandle(sender.Handle(), source_handle, + base::GetCurrentProcessHandle(), &target_handle, 0, + FALSE, DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE); + DCHECK(dup_result); + rv->push_back(embedder::PlatformHandle(target_handle)); + } + return rv.Pass(); } RawChannel::IOResult RawChannelWin::WriteNoLock( @@ -450,9 +478,28 @@ DCHECK(io_handler_); DCHECK(!io_handler_->pending_write_no_lock()); + size_t num_platform_handles = 0; if (write_buffer_no_lock()->HavePlatformHandlesToSend()) { - // TODO(vtl): Implement. - NOTIMPLEMENTED(); + // Since we're not sure which process might ultimately deserialize this + // message, we can't duplicate the handle now. Instead, write the process ID + // and handle now and let the receiver duplicate it. + embedder::PlatformHandle* platform_handles; + void* serialization_data_temp; + write_buffer_no_lock()->GetPlatformHandlesToSend( + &num_platform_handles, &platform_handles, &serialization_data_temp); + char* serialization_data = static_cast<char*>(serialization_data_temp); + DCHECK_GT(num_platform_handles, 0u); + DCHECK(platform_handles); + + DWORD current_process_id = base::GetCurrentProcId(); + for (size_t i = 0; i < num_platform_handles; i++) { + *reinterpret_cast<DWORD*>(serialization_data) = current_process_id; + serialization_data += sizeof(DWORD); + *reinterpret_cast<HANDLE*>(serialization_data) = + platform_handles[i].handle; + serialization_data += sizeof(HANDLE); + platform_handles[i] = embedder::PlatformHandle(); + } } std::vector<WriteBuffer::Buffer> buffers; @@ -476,7 +523,7 @@ } if (result && skip_completion_port_on_success_) { - *platform_handles_written = 0; + *platform_handles_written = num_platform_handles; *bytes_written = bytes_written_dword; return IO_SUCCEEDED; }
diff --git a/third_party/mojo_services/src/surfaces/public/cpp/BUILD.gn b/third_party/mojo_services/src/surfaces/public/cpp/BUILD.gn deleted file mode 100644 index 9cf48b33..0000000 --- a/third_party/mojo_services/src/surfaces/public/cpp/BUILD.gn +++ /dev/null
@@ -1,20 +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. - -import("//build/module_args/mojo.gni") -import("$mojo_sdk_root/mojo/public/mojo_sdk.gni") - -mojo_sdk_source_set("cpp") { - restrict_external_deps = false - public_configs = [ "../../../public/build/config:mojo_services" ] - sources = [ - "surfaces_utils.cc", - "surfaces_utils.h", - ] - - deps = [ - "../../../geometry/public/interfaces", - "../../../surfaces/public/interfaces", - ] -}
diff --git a/third_party/mojo_services/src/surfaces/public/cpp/surfaces_utils.cc b/third_party/mojo_services/src/surfaces/public/cpp/surfaces_utils.cc deleted file mode 100644 index 2e52fd26..0000000 --- a/third_party/mojo_services/src/surfaces/public/cpp/surfaces_utils.cc +++ /dev/null
@@ -1,49 +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 "surfaces/public/cpp/surfaces_utils.h" - -#include "geometry/public/interfaces/geometry.mojom.h" - -namespace mojo { - -namespace { -TransformPtr GetIdentityTransform() { - TransformPtr transform(Transform::New()); - transform->matrix.resize(16); - transform->matrix[0] = 1.f; - transform->matrix[5] = 1.f; - transform->matrix[10] = 1.f; - transform->matrix[15] = 1.f; - return transform.Pass(); -} -} - -SharedQuadStatePtr CreateDefaultSQS(const Size& size) { - SharedQuadStatePtr sqs = SharedQuadState::New(); - sqs->content_to_target_transform = GetIdentityTransform(); - sqs->content_bounds = size.Clone(); - Rect rect; - rect.width = size.width; - rect.height = size.height; - sqs->visible_content_rect = rect.Clone(); - sqs->clip_rect = rect.Clone(); - sqs->is_clipped = false; - sqs->opacity = 1.f; - sqs->blend_mode = mojo::SK_XFERMODE_kSrc_Mode; - sqs->sorting_context_id = 0; - return sqs.Pass(); -} - -PassPtr CreateDefaultPass(int id, const Rect& rect) { - PassPtr pass = Pass::New(); - pass->id = id; - pass->output_rect = rect.Clone(); - pass->damage_rect = rect.Clone(); - pass->transform_to_root_target = GetIdentityTransform(); - pass->has_transparent_background = false; - return pass.Pass(); -} - -} // namespace mojo
diff --git a/third_party/mojo_services/src/surfaces/public/cpp/surfaces_utils.h b/third_party/mojo_services/src/surfaces/public/cpp/surfaces_utils.h deleted file mode 100644 index f74731e..0000000 --- a/third_party/mojo_services/src/surfaces/public/cpp/surfaces_utils.h +++ /dev/null
@@ -1,23 +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_SERVICES_SURFACES_PUBLIC_CPP_SURFACES_UTILS_H_ -#define MOJO_SERVICES_SURFACES_PUBLIC_CPP_SURFACES_UTILS_H_ - -#include "surfaces/public/interfaces/quads.mojom.h" - -namespace mojo { -class Rect; -class Size; - -SharedQuadStatePtr CreateDefaultSQS(const Size& size); - -// Constructs a pass with the given id, output_rect and damage_rect set to rect, -// transform_to_root_target set to identity and has_transparent_background set -// to false. -PassPtr CreateDefaultPass(int id, const Rect& rect); - -} // namespace mojo - -#endif // MOJO_SERVICES_SURFACES_PUBLIC_CPP_SURFACES_UTILS_H_
diff --git a/third_party/mojo_services/src/surfaces/public/interfaces/quads.mojom b/third_party/mojo_services/src/surfaces/public/interfaces/quads.mojom index 2418d51..3022df0a 100644 --- a/third_party/mojo_services/src/surfaces/public/interfaces/quads.mojom +++ b/third_party/mojo_services/src/surfaces/public/interfaces/quads.mojom
@@ -202,7 +202,7 @@ }; struct Pass { - int32 id; + RenderPassId id; Rect output_rect; Rect damage_rect; Transform transform_to_root_target;
diff --git a/third_party/opus/opus_nacl.gyp b/third_party/opus/opus_nacl.gyp index c4287c27..27dc55c 100644 --- a/third_party/opus/opus_nacl.gyp +++ b/third_party/opus/opus_nacl.gyp
@@ -16,9 +16,6 @@ 'build_newlib': 0, 'build_pnacl_newlib': 1, }, - 'dependencies': [ - '<(DEPTH)/native_client/tools.gyp:prep_toolchain', - ], 'defines': [ 'OPUS_BUILD', 'OPUS_EXPORT=',
diff --git a/third_party/protobuf/protobuf_nacl.gyp b/third_party/protobuf/protobuf_nacl.gyp index e8741c2..ae37f2f 100644 --- a/third_party/protobuf/protobuf_nacl.gyp +++ b/third_party/protobuf/protobuf_nacl.gyp
@@ -17,9 +17,6 @@ 'build_pnacl_newlib': 1, 'config_h_dir': '.', }, - 'dependencies': [ - '../../native_client/tools.gyp:prep_toolchain', - ], 'pnacl_compile_flags': [ # This disables #warning in hash_map/hash_set headers which are # deprecated but still used in protobuf.
diff --git a/third_party/re2/README.chromium b/third_party/re2/README.chromium index 34c6d5e..56a3f811 100644 --- a/third_party/re2/README.chromium +++ b/third_party/re2/README.chromium
@@ -18,7 +18,6 @@ - Support for Windows (patches/re2-msvc9-chrome.patch) - Support Android (patches/re2-android.patch) - Remove static initializers (patches/remove-static-initializers.patch) -- Rename POSIX configuration (patches/rename-posix-option.patch) - Support libcxx (patches/re2-libcxx.patch) https://code.google.com/p/re2/issues/detail?id=76 - Memory optimization for filtered trees @@ -30,3 +29,4 @@ - Remove comparisons of this with NULL, merges upstream b92ce81f1e25 - Let COMPILE_ASSERT use static_assert if available, merges upstream 2225f94df8ec +- Merge upstream cc56ba02d9d2bdafa614ad5ebf564dde287625bb.
diff --git a/third_party/re2/patches/re2-memory-optimization.patch b/third_party/re2/patches/re2-memory-optimization.patch deleted file mode 100644 index 05299b39..0000000 --- a/third_party/re2/patches/re2-memory-optimization.patch +++ /dev/null
@@ -1,133 +0,0 @@ -diff --git a/re2/prefilter_tree.cc b/re2/prefilter_tree.cc ---- a/re2/prefilter_tree.cc -+++ b/re2/prefilter_tree.cc -@@ -107,21 +107,23 @@ void PrefilterTree::Compile(vector<string>* atom_vec) { - // not miss out on any regexps triggering by getting rid of a - // prefilter node. - for (int i = 0; i < entries_.size(); i++) { -- IntMap* parents = entries_[i].parents; -+ StdIntMap* parents = entries_[i].parents; - if (parents->size() > 8) { - // This one triggers too many things. If all the parents are AND - // nodes and have other things guarding them, then get rid of - // this trigger. TODO(vsri): Adjust the threshold appropriately, - // make it a function of total number of nodes? - bool have_other_guard = true; -- for (IntMap::iterator it = parents->begin(); it != parents->end(); ++it) -+ for (StdIntMap::iterator it = parents->begin(); -+ it != parents->end(); ++it) { - have_other_guard = have_other_guard && -- (entries_[it->index()].propagate_up_at_count > 1); -+ (entries_[it->first].propagate_up_at_count > 1); -+ } - - if (have_other_guard) { -- for (IntMap::iterator it = parents->begin(); -+ for (StdIntMap::iterator it = parents->begin(); - it != parents->end(); ++it) -- entries_[it->index()].propagate_up_at_count -= 1; -+ entries_[it->first].propagate_up_at_count -= 1; - - parents->clear(); // Forget the parents - } -@@ -213,7 +215,7 @@ void PrefilterTree::AssignUniqueIds(vector<string>* atom_vec) { - } - entries_.resize(node_map_.size()); - -- // Create parent IntMap for the entries. -+ // Create parent StdIntMap for the entries. - for (int i = v.size() - 1; i >= 0; i--) { - Prefilter* prefilter = v[i]; - if (prefilter == NULL) -@@ -223,7 +225,7 @@ void PrefilterTree::AssignUniqueIds(vector<string>* atom_vec) { - continue; - - Entry* entry = &entries_[prefilter->unique_id()]; -- entry->parents = new IntMap(node_map_.size()); -+ entry->parents = new StdIntMap(); - } - - // Fill the entries. -@@ -249,7 +251,7 @@ void PrefilterTree::AssignUniqueIds(vector<string>* atom_vec) { - - case Prefilter::OR: - case Prefilter::AND: { -- IntMap uniq_child(node_map_.size()); -+ std::set<int> uniq_child; - for (int j = 0; j < prefilter->subs()->size() ; j++) { - Prefilter* child = (*prefilter->subs())[j]; - Prefilter* canonical = CanonicalNode(child); -@@ -258,12 +260,12 @@ void PrefilterTree::AssignUniqueIds(vector<string>* atom_vec) { - return; - } - int child_id = canonical->unique_id(); -- if (!uniq_child.has_index(child_id)) -- uniq_child.set_new(child_id, 1); -+ uniq_child.insert(child_id); - // To the child, we want to add to parent indices. - Entry* child_entry = &entries_[child_id]; -- if (!child_entry->parents->has_index(prefilter->unique_id())) -- child_entry->parents->set_new(prefilter->unique_id(), 1); -+ if (child_entry->parents->find(prefilter->unique_id()) == -+ child_entry->parents->end()) -+ (*child_entry->parents)[prefilter->unique_id()] = 1; - } - entry->propagate_up_at_count = - prefilter->op() == Prefilter::AND ? uniq_child.size() : 1; -@@ -329,10 +331,10 @@ void PrefilterTree::PropagateMatch(const vector<int>& atom_ids, - } - int c; - // Pass trigger up to parents. -- for (IntMap::iterator it = entry.parents->begin(); -+ for (StdIntMap::iterator it = entry.parents->begin(); - it != entry.parents->end(); - ++it) { -- int j = it->index(); -+ int j = it->first; - const Entry& parent = entries_[j]; - VLOG(10) << " parent= " << j << " trig= " << parent.propagate_up_at_count; - // Delay until all the children have succeeded. -@@ -364,12 +366,12 @@ void PrefilterTree::PrintDebugInfo() { - VLOG(10) << "#Unique Nodes: " << entries_.size(); - - for (int i = 0; i < entries_.size(); ++i) { -- IntMap* parents = entries_[i].parents; -+ StdIntMap* parents = entries_[i].parents; - const vector<int>& regexps = entries_[i].regexps; - VLOG(10) << "EntryId: " << i - << " N: " << parents->size() << " R: " << regexps.size(); -- for (IntMap::iterator it = parents->begin(); it != parents->end(); ++it) -- VLOG(10) << it->index(); -+ for (StdIntMap::iterator it = parents->begin(); it != parents->end(); ++it) -+ VLOG(10) << it->first; - } - VLOG(10) << "Map:"; - for (map<string, Prefilter*>::const_iterator iter = node_map_.begin(); -diff --git a/re2/prefilter_tree.h b/re2/prefilter_tree.h ---- a/re2/prefilter_tree.h -+++ b/re2/prefilter_tree.h -@@ -16,12 +16,15 @@ - #ifndef RE2_PREFILTER_TREE_H_ - #define RE2_PREFILTER_TREE_H_ - -+#include <map> -+ - #include "util/util.h" - #include "util/sparse_array.h" - - namespace re2 { - - typedef SparseArray<int> IntMap; -+typedef std::map<int, int> StdIntMap; - - class Prefilter; - -@@ -71,7 +74,7 @@ class PrefilterTree { - // are two different nodes, but they share the atom 'def'. So when - // 'def' matches, it triggers two parents, corresponding to the two - // different OR nodes. -- IntMap* parents; -+ StdIntMap* parents; - - // When this node is ready to trigger the parent, what are the - // regexps that are triggered.
diff --git a/third_party/re2/patches/remove-static-initializers.patch b/third_party/re2/patches/remove-static-initializers.patch deleted file mode 100644 index c9ef9e3..0000000 --- a/third_party/re2/patches/remove-static-initializers.patch +++ /dev/null
@@ -1,95 +0,0 @@ -diff --git a/re2/compile.cc b/re2/compile.cc -index adb45fd..14e401a 100644 ---- a/re2/compile.cc -+++ b/re2/compile.cc -@@ -44,7 +44,7 @@ struct PatchList { - static PatchList Append(Prog::Inst *inst0, PatchList l1, PatchList l2); - }; - --static PatchList nullPatchList; -+static PatchList nullPatchList = { 0 }; - - // Returns patch list containing just p. - PatchList PatchList::Mk(uint32 p) { -@@ -106,12 +106,13 @@ struct Frag { - uint32 begin; - PatchList end; - -- explicit Frag(LinkerInitialized) {} - Frag() : begin(0) { end.p = 0; } // needed so Frag can go in vector - Frag(uint32 begin, PatchList end) : begin(begin), end(end) {} - }; - --static Frag kNullFrag(LINKER_INITIALIZED); -+static Frag NullFrag() { -+ return Frag(); -+} - - // Input encodings. - enum Encoding { -@@ -684,13 +685,13 @@ Frag Compiler::PreVisit(Regexp* re, Frag, bool* stop) { - if (failed_) - *stop = true; - -- return kNullFrag; // not used by caller -+ return NullFrag(); // not used by caller - } - - Frag Compiler::Literal(Rune r, bool foldcase) { - switch (encoding_) { - default: -- return kNullFrag; -+ return NullFrag(); - - case kEncodingLatin1: - return ByteRange(r, r, foldcase); -@@ -1006,7 +1007,7 @@ Prog* Compiler::Compile(Regexp* re, bool reversed, int64 max_mem) { - bool is_anchor_end = IsAnchorEnd(&sre, 0); - - // Generate fragment for entire regexp. -- Frag f = c.WalkExponential(sre, kNullFrag, 2*c.max_inst_); -+ Frag f = c.WalkExponential(sre, NullFrag(), 2*c.max_inst_); - sre->Decref(); - if (c.failed_) - return NULL; -@@ -1097,7 +1098,7 @@ Prog* Compiler::CompileSet(const RE2::Options& options, RE2::Anchor anchor, - c.Setup(pf, options.max_mem(), anchor); - - // Compile alternation of fragments. -- Frag all = c.WalkExponential(re, kNullFrag, 2*c.max_inst_); -+ Frag all = c.WalkExponential(re, NullFrag(), 2*c.max_inst_); - re->Decref(); - if (c.failed_) - return NULL; -diff --git a/re2/re2.cc b/re2/re2.cc -index 0da886d..b9e44fc 100644 ---- a/re2/re2.cc -+++ b/re2/re2.cc -@@ -32,10 +32,10 @@ namespace re2 { - static const int kMaxArgs = 16; - static const int kVecSize = 1+kMaxArgs; - --const VariadicFunction2<bool, const StringPiece&, const RE2&, RE2::Arg, RE2::FullMatchN> RE2::FullMatch; --const VariadicFunction2<bool, const StringPiece&, const RE2&, RE2::Arg, RE2::PartialMatchN> RE2::PartialMatch; --const VariadicFunction2<bool, StringPiece*, const RE2&, RE2::Arg, RE2::ConsumeN> RE2::Consume; --const VariadicFunction2<bool, StringPiece*, const RE2&, RE2::Arg, RE2::FindAndConsumeN> RE2::FindAndConsume; -+const VariadicFunction2<bool, const StringPiece&, const RE2&, RE2::Arg, RE2::FullMatchN> RE2::FullMatch = {}; -+const VariadicFunction2<bool, const StringPiece&, const RE2&, RE2::Arg, RE2::PartialMatchN> RE2::PartialMatch = {}; -+const VariadicFunction2<bool, StringPiece*, const RE2&, RE2::Arg, RE2::ConsumeN> RE2::Consume = {}; -+const VariadicFunction2<bool, StringPiece*, const RE2&, RE2::Arg, RE2::FindAndConsumeN> RE2::FindAndConsume = {}; - - #define kDefaultMaxMem (8<<20) - -diff --git a/re2/variadic_function.h b/re2/variadic_function.h -index 8d2b763..7c7d6d5 100644 ---- a/re2/variadic_function.h -+++ b/re2/variadic_function.h -@@ -11,8 +11,6 @@ template <typename Result, typename Param0, typename Param1, typename Arg, - Result (*Func)(Param0, Param1, const Arg* const [], int count)> - class VariadicFunction2 { - public: -- VariadicFunction2() {} -- - Result operator()(Param0 p0, Param1 p1) const { - return Func(p0, p1, 0, 0); - }
diff --git a/third_party/re2/patches/rename-posix-option.patch b/third_party/re2/patches/rename-posix-option.patch deleted file mode 100644 index 47caa63..0000000 --- a/third_party/re2/patches/rename-posix-option.patch +++ /dev/null
@@ -1,28 +0,0 @@ -diff --git a/re2/re2.cc b/re2/re2.cc -index b9e44fc..fb43abf 100644 ---- a/re2/re2.cc -+++ b/re2/re2.cc -@@ -56,8 +56,8 @@ RE2::Options::Options() - - RE2::Options::Options(RE2::CannedOptions opt) - : encoding_(opt == RE2::Latin1 ? EncodingLatin1 : EncodingUTF8), -- posix_syntax_(opt == RE2::POSIX), -- longest_match_(opt == RE2::POSIX), -+ posix_syntax_(opt == RE2::POSIX_SYNTAX), -+ longest_match_(opt == RE2::POSIX_SYNTAX), - log_errors_(opt != RE2::Quiet), - max_mem_(kDefaultMaxMem), - literal_(false), -diff --git a/re2/re2.h b/re2/re2.h -index c509853..98b06b8 100644 ---- a/re2/re2.h -+++ b/re2/re2.h -@@ -251,7 +251,7 @@ class RE2 { - enum CannedOptions { - DefaultOptions = 0, - Latin1, // treat input as Latin-1 (default UTF-8) -- POSIX, // POSIX syntax, leftmost-longest match -+ POSIX_SYNTAX, // POSIX syntax, leftmost-longest match - Quiet // do not log about regexp parse errors - }; -
diff --git a/third_party/re2/re2/compile.cc b/third_party/re2/re2/compile.cc index 14e401a..9a59f13 100644 --- a/third_party/re2/re2/compile.cc +++ b/third_party/re2/re2/compile.cc
@@ -110,10 +110,6 @@ Frag(uint32 begin, PatchList end) : begin(begin), end(end) {} }; -static Frag NullFrag() { - return Frag(); -} - // Input encodings. enum Encoding { kEncodingUTF8 = 1, // UTF-8 (0-10FFFF) @@ -685,13 +681,13 @@ if (failed_) *stop = true; - return NullFrag(); // not used by caller + return Frag(); // not used by caller } Frag Compiler::Literal(Rune r, bool foldcase) { switch (encoding_) { default: - return NullFrag(); + return Frag(); case kEncodingLatin1: return ByteRange(r, r, foldcase); @@ -1007,7 +1003,7 @@ bool is_anchor_end = IsAnchorEnd(&sre, 0); // Generate fragment for entire regexp. - Frag f = c.WalkExponential(sre, NullFrag(), 2*c.max_inst_); + Frag f = c.WalkExponential(sre, Frag(), 2*c.max_inst_); sre->Decref(); if (c.failed_) return NULL; @@ -1098,7 +1094,7 @@ c.Setup(pf, options.max_mem(), anchor); // Compile alternation of fragments. - Frag all = c.WalkExponential(re, NullFrag(), 2*c.max_inst_); + Frag all = c.WalkExponential(re, Frag(), 2*c.max_inst_); re->Decref(); if (c.failed_) return NULL;
diff --git a/third_party/re2/re2/re2.cc b/third_party/re2/re2/re2.cc index fb43abf..b9e44fc 100644 --- a/third_party/re2/re2/re2.cc +++ b/third_party/re2/re2/re2.cc
@@ -56,8 +56,8 @@ RE2::Options::Options(RE2::CannedOptions opt) : encoding_(opt == RE2::Latin1 ? EncodingLatin1 : EncodingUTF8), - posix_syntax_(opt == RE2::POSIX_SYNTAX), - longest_match_(opt == RE2::POSIX_SYNTAX), + posix_syntax_(opt == RE2::POSIX), + longest_match_(opt == RE2::POSIX), log_errors_(opt != RE2::Quiet), max_mem_(kDefaultMaxMem), literal_(false),
diff --git a/third_party/re2/re2/re2.h b/third_party/re2/re2/re2.h index 98b06b86..c509853 100644 --- a/third_party/re2/re2/re2.h +++ b/third_party/re2/re2/re2.h
@@ -251,7 +251,7 @@ enum CannedOptions { DefaultOptions = 0, Latin1, // treat input as Latin-1 (default UTF-8) - POSIX_SYNTAX, // POSIX syntax, leftmost-longest match + POSIX, // POSIX syntax, leftmost-longest match Quiet // do not log about regexp parse errors };
diff --git a/third_party/re2/util/util.h b/third_party/re2/util/util.h index 8350445..21439e97 100644 --- a/third_party/re2/util/util.h +++ b/third_party/re2/util/util.h
@@ -46,8 +46,7 @@ using std::swap; using std::make_pair; -#if defined(__GNUC__) && !defined(USE_CXX0X) && !defined(OS_ANDROID) && \ - !defined(_LIBCPP_ABI_VERSION) +#if defined(__GNUC__) && !defined(USE_CXX0X) && !defined(_LIBCPP_ABI_VERSION) && !defined(OS_ANDROID) #include <tr1/unordered_set> using std::tr1::unordered_set;
diff --git a/build/secondary/third_party/sfntly/BUILD.gn b/third_party/sfntly/BUILD.gn similarity index 100% rename from build/secondary/third_party/sfntly/BUILD.gn rename to third_party/sfntly/BUILD.gn
diff --git a/third_party/usrsctp/usrsctp_nacl.gyp b/third_party/usrsctp/usrsctp_nacl.gyp index bf5c84f9..7cce5e7 100644 --- a/third_party/usrsctp/usrsctp_nacl.gyp +++ b/third_party/usrsctp/usrsctp_nacl.gyp
@@ -16,7 +16,6 @@ 'build_pnacl_newlib': 1, }, 'dependencies': [ - '<(DEPTH)/native_client/tools.gyp:prep_toolchain', '<(DEPTH)/native_client_sdk/native_client_sdk_untrusted.gyp:nacl_io_untrusted', '<(DEPTH)/third_party/boringssl/boringssl_nacl.gyp:boringssl_nacl', ],
diff --git a/third_party/win_toolchain/LICENSE b/third_party/win_toolchain/LICENSE deleted file mode 100644 index 3d0f7d3e..0000000 --- a/third_party/win_toolchain/LICENSE +++ /dev/null
@@ -1,27 +0,0 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/third_party/win_toolchain/README.chromium b/third_party/win_toolchain/README.chromium deleted file mode 100644 index e32ec766..0000000 --- a/third_party/win_toolchain/README.chromium +++ /dev/null
@@ -1,6 +0,0 @@ -Name: SHA1 for Windows toolchain that is built from external sources. -Short Name: win_toolchain -URL: http://msdn.microsoft.com/ -License: BSD 3-Clause License -License File: LICENSE -Security Critical: yes
diff --git a/third_party/win_toolchain/toolchain.sha1 b/third_party/win_toolchain/toolchain.sha1 deleted file mode 100644 index 31489690..0000000 --- a/third_party/win_toolchain/toolchain.sha1 +++ /dev/null
@@ -1 +0,0 @@ -1b78fa680523f420bbfb75371ea76959eabafffa
diff --git a/tools/clang/blink_gc_plugin/Edge.h b/tools/clang/blink_gc_plugin/Edge.h index 7659968..2ea8e54 100644 --- a/tools/clang/blink_gc_plugin/Edge.h +++ b/tools/clang/blink_gc_plugin/Edge.h
@@ -39,14 +39,14 @@ class RecursiveEdgeVisitor : public EdgeVisitor { public: // Overrides that recursively walk the edges and record the path. - virtual void VisitValue(Value*) override; - virtual void VisitRawPtr(RawPtr*) override; - virtual void VisitRefPtr(RefPtr*) override; - virtual void VisitOwnPtr(OwnPtr*) override; - virtual void VisitMember(Member*) override; - virtual void VisitWeakMember(WeakMember*) override; - virtual void VisitPersistent(Persistent*) override; - virtual void VisitCollection(Collection*) override; + void VisitValue(Value*) override; + void VisitRawPtr(RawPtr*) override; + void VisitRefPtr(RefPtr*) override; + void VisitOwnPtr(OwnPtr*) override; + void VisitMember(Member*) override; + void VisitWeakMember(WeakMember*) override; + void VisitPersistent(Persistent*) override; + void VisitCollection(Collection*) override; protected: typedef std::deque<Edge*> Context;
diff --git a/tools/clang/blink_gc_plugin/JsonWriter.h b/tools/clang/blink_gc_plugin/JsonWriter.h index 3fe7910..09504b3 100644 --- a/tools/clang/blink_gc_plugin/JsonWriter.h +++ b/tools/clang/blink_gc_plugin/JsonWriter.h
@@ -7,12 +7,24 @@ #include "llvm/Support/raw_ostream.h" +// TODO(hans): Remove this #ifdef after Clang is rolled past r234897. +#ifdef LLVM_FORCE_HEAD_REVISION +#define JSON_WRITER_STREAM std::unique_ptr<llvm::raw_ostream> +#else +#define JSON_WRITER_STREAM llvm::raw_fd_ostream* +#endif + // Helper to write information for the points-to graph. class JsonWriter { public: - static JsonWriter* from(std::unique_ptr<llvm::raw_ostream> os) { + static JsonWriter* from(JSON_WRITER_STREAM os) { return os ? new JsonWriter(std::move(os)) : 0; } +#ifndef LLVM_FORCE_HEAD_REVISION + ~JsonWriter() { + delete os_; + } +#endif void OpenList() { Separator(); *os_ << "["; @@ -53,7 +65,7 @@ *os_ << "\"" << key << "\":\"" << val << "\""; } private: - JsonWriter(std::unique_ptr<llvm::raw_ostream> os) : os_(std::move(os)) {} + JsonWriter(JSON_WRITER_STREAM os) : os_(std::move(os)) {} void Separator() { if (state_.empty()) return; @@ -63,7 +75,7 @@ } state_.top() = true; } - std::unique_ptr<llvm::raw_ostream> os_; + JSON_WRITER_STREAM os_; std::stack<bool> state_; };
diff --git a/tools/clang/plugins/ChromeClassTester.cpp b/tools/clang/plugins/ChromeClassTester.cpp index b9d49c3..931da02 100644 --- a/tools/clang/plugins/ChromeClassTester.cpp +++ b/tools/clang/plugins/ChromeClassTester.cpp
@@ -107,6 +107,55 @@ DiagnosticBuilder builder = diagnostic().Report(full, id); } +bool ChromeClassTester::InBannedDirectory(SourceLocation loc) { + if (instance().getSourceManager().isInSystemHeader(loc)) + return true; + + std::string filename; + if (!GetFilename(loc, &filename)) { + // If the filename cannot be determined, simply treat this as a banned + // location, instead of going through the full lookup process. + return true; + } + + // We need to special case scratch space; which is where clang does its + // macro expansion. We explicitly want to allow people to do otherwise bad + // things through macros that were defined due to third party libraries. + if (filename == "<scratch space>") + return true; + + // Don't complain about autogenerated protobuf files. + if (ends_with(filename, ".pb.h")) { + return true; + } + +#if defined(LLVM_ON_UNIX) + // We need to munge the paths so that they are relative to the repository + // srcroot. We first resolve the symlinktastic relative path and then + // remove our known srcroot from it if needed. + char resolvedPath[MAXPATHLEN]; + if (realpath(filename.c_str(), resolvedPath)) { + filename = resolvedPath; + } +#endif + +#if defined(LLVM_ON_WIN32) + std::replace(filename.begin(), filename.end(), '\\', '/'); +#endif + + for (const std::string& banned_dir : banned_directories_) { + // If any of the banned directories occur as a component in filename, + // this file is rejected. + assert(banned_dir.front() == '/' && "Banned dir must start with '/'"); + assert(banned_dir.back() == '/' && "Banned dir must end with '/'"); + + if (filename.find(banned_dir) != std::string::npos) + return true; + } + + return false; +} + bool ChromeClassTester::InBannedNamespace(const Decl* record) { std::string n = GetNamespace(record); if (!n.empty()) { @@ -237,55 +286,6 @@ } } -bool ChromeClassTester::InBannedDirectory(SourceLocation loc) { - if (instance().getSourceManager().isInSystemHeader(loc)) - return true; - - std::string filename; - if (!GetFilename(loc, &filename)) { - // If the filename cannot be determined, simply treat this as a banned - // location, instead of going through the full lookup process. - return true; - } - - // We need to special case scratch space; which is where clang does its - // macro expansion. We explicitly want to allow people to do otherwise bad - // things through macros that were defined due to third party libraries. - if (filename == "<scratch space>") - return true; - - // Don't complain about autogenerated protobuf files. - if (ends_with(filename, ".pb.h")) { - return true; - } - -#if defined(LLVM_ON_UNIX) - // We need to munge the paths so that they are relative to the repository - // srcroot. We first resolve the symlinktastic relative path and then - // remove our known srcroot from it if needed. - char resolvedPath[MAXPATHLEN]; - if (realpath(filename.c_str(), resolvedPath)) { - filename = resolvedPath; - } -#endif - -#if defined(LLVM_ON_WIN32) - std::replace(filename.begin(), filename.end(), '\\', '/'); -#endif - - for (const std::string& banned_dir : banned_directories_) { - // If any of the banned directories occur as a component in filename, - // this file is rejected. - assert(banned_dir.front() == '/' && "Banned dir must start with '/'"); - assert(banned_dir.back() == '/' && "Banned dir must end with '/'"); - - if (filename.find(banned_dir) != std::string::npos) - return true; - } - - return false; -} - bool ChromeClassTester::IsIgnoredType(const std::string& base_name) { return ignored_record_names_.find(base_name) != ignored_record_names_.end(); }
diff --git a/tools/clang/plugins/ChromeClassTester.h b/tools/clang/plugins/ChromeClassTester.h index 963b90f..ed65050 100644 --- a/tools/clang/plugins/ChromeClassTester.h +++ b/tools/clang/plugins/ChromeClassTester.h
@@ -37,6 +37,10 @@ // namespace. bool InBannedNamespace(const clang::Decl* record); + // Utility method for subclasses to check if the source location is in a + // directory the plugin should ignore. + bool InBannedDirectory(clang::SourceLocation loc); + // Utility method for subclasses to determine the namespace of the // specified record, if any. Unnamed namespaces will be identified as // "<anonymous namespace>". @@ -64,7 +68,6 @@ // deliberately ignore) in HandleTagDeclDefinition(). std::string GetNamespaceImpl(const clang::DeclContext* context, const std::string& candidate); - bool InBannedDirectory(clang::SourceLocation loc); bool IsIgnoredType(const std::string& base_name); // Attempts to determine the filename for the given SourceLocation.
diff --git a/tools/clang/plugins/FindBadConstructsConsumer.cpp b/tools/clang/plugins/FindBadConstructsConsumer.cpp index 2524550e..20919a4 100644 --- a/tools/clang/plugins/FindBadConstructsConsumer.cpp +++ b/tools/clang/plugins/FindBadConstructsConsumer.cpp
@@ -70,14 +70,15 @@ return decl->getQualifiedNameAsString() == "testing::Test"; } +// Generates a fixit hint to remove the 'virtual' keyword. +// Unfortunately, there doesn't seem to be a good way to determine the source +// location of the 'virtual' keyword. It's available in Declarator, but that +// isn't accessible from the AST. So instead, make an educated guess that the +// first token is probably the virtual keyword. Strictly speaking, this doesn't +// have to be true, but it probably will be. +// TODO(dcheng): Add a warning to force virtual to always appear first ;-) FixItHint FixItRemovalForVirtual(const SourceManager& manager, const CXXMethodDecl* method) { - // Unfortunately, there doesn't seem to be a good way to determine the - // location of the 'virtual' keyword. It's available in Declarator, but that - // isn't accessible from the AST. So instead, make an educated guess that the - // first token is probably the virtual keyword. Strictly speaking, this - // doesn't have to be true, but it probably will be. - // TODO(dcheng): Add a warning to force virtual to always appear first ;-) SourceRange range(method->getLocStart()); // Get the spelling loc just in case it was expanded from a macro. SourceRange spelling_range(manager.getSpellingLoc(range.getBegin())); @@ -413,32 +414,68 @@ // Complain if a method is annotated virtual && (override || final). if (has_virtual && (override_attr || final_attr)) { - diagnostic().Report(method->getLocStart(), - diag_redundant_virtual_specifier_) - << "'virtual'" - << (override_attr ? static_cast<Attr*>(override_attr) : final_attr) - << FixItRemovalForVirtual(manager, method); + // ... but only if virtual does not originate in a macro from a banned file. + // Note this is just an educated guess: the assumption here is that any + // macro for declaring methods will probably be at the start of the method's + // source range. + if (!InBannedDirectory(manager.getSpellingLoc(method->getLocStart()))) { + diagnostic().Report(method->getLocStart(), + diag_redundant_virtual_specifier_) + << "'virtual'" + << (override_attr ? static_cast<Attr*>(override_attr) : final_attr) + << FixItRemovalForVirtual(manager, method); + } } // Complain if a method is an override and is not annotated with override or // final. if (is_override && !override_attr && !final_attr) { - SourceRange type_info_range = - method->getTypeSourceInfo()->getTypeLoc().getSourceRange(); - FullSourceLoc loc(type_info_range.getBegin(), manager); - - // Build the FixIt insertion point after the end of the method definition, - // including any const-qualifiers and attributes, and before the opening - // of the l-curly-brace (if inline) or the semi-color (if a declaration). - SourceLocation spelling_end = - manager.getSpellingLoc(type_info_range.getEnd()); - if (spelling_end.isValid()) { - SourceLocation token_end = - Lexer::getLocForEndOfToken(spelling_end, 0, manager, LangOptions()); - diagnostic().Report(token_end, diag_method_requires_override_) - << FixItHint::CreateInsertion(token_end, " override"); + SourceRange range = method->getSourceRange(); + SourceLocation loc; + if (method->hasInlineBody()) { + loc = method->getBody()->getSourceRange().getBegin(); } else { - diagnostic().Report(loc, diag_method_requires_override_); + // TODO(dcheng): We should probably use ASTContext's LangOptions here. + LangOptions lang_options; + loc = Lexer::getLocForEndOfToken( + manager.getSpellingLoc(range.getEnd()), 0, + manager, lang_options); + // The original code used the ending source loc of TypeSourceInfo's + // TypeLoc. Unfortunately, this breaks down in the presence of attributes. + // Attributes often appear at the end of a TypeLoc, e.g. + // virtual ULONG __stdcall AddRef() + // has a TypeSourceInfo that looks something like: + // ULONG AddRef() __attribute(stdcall) + // so a fix-it insertion would be generated to insert 'override' after + // __stdcall in the code as written. + // While using the spelling loc of the CXXMethodDecl fixes attribute + // handling, it breaks handling of "= 0" and similar constructs.. To work + // around this, scan backwards in the source text for a '=' or ')' token + // and adjust the location as needed... + for (SourceLocation l = loc.getLocWithOffset(-1); + l != manager.getLocForStartOfFile(manager.getFileID(loc)); + l = l.getLocWithOffset(-1)) { + l = Lexer::GetBeginningOfToken(l, manager, lang_options); + Token token; + // getRawToken() returns *true* on failure. In that case, just give up + // and don't bother generating a possibly incorrect fix-it. + if (Lexer::getRawToken(l, token, manager, lang_options, true)) { + loc = SourceLocation(); + break; + } + if (token.is(tok::r_paren)) { + break; + } else if (token.is(tok::equal)) { + loc = l; + break; + } + } + } + if (loc.isValid()) { + diagnostic().Report(loc, diag_method_requires_override_) + << FixItHint::CreateInsertion(loc, " override"); + } else { + diagnostic().Report(range.getBegin(), diag_method_requires_override_); } }
diff --git a/tools/clang/plugins/tests/overridden_methods.txt b/tools/clang/plugins/tests/overridden_methods.txt index 69ff2b1..bc2d1aca 100644 --- a/tools/clang/plugins/tests/overridden_methods.txt +++ b/tools/clang/plugins/tests/overridden_methods.txt
@@ -1,82 +1,82 @@ In file included from overridden_methods.cpp:5: -./overridden_methods.h:25:28: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'. +./overridden_methods.h:25:29: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'. virtual void SomeMethod() = 0; - ^ - override -./overridden_methods.h:46:26: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'. + ^ + override +./overridden_methods.h:46:27: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'. virtual ~DerivedClass() {} - ^ - override + ^ + override ./overridden_methods.h:48:28: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'. virtual void SomeMethod(); ^ override -./overridden_methods.h:52:34: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'. +./overridden_methods.h:52:35: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'. virtual void SomeInlineMethod() {} - ^ - override -./overridden_methods.h:56:39: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'. + ^ + override +./overridden_methods.h:56:40: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'. virtual void SomeConstMethod() const {} - ^ - override -./overridden_methods.h:58:55: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'. + ^ + override +./overridden_methods.h:58:54: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'. virtual void SomeMethodWithExceptionSpec() throw() {} - ^ - override -./overridden_methods.h:61:69: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'. + ^ + override +./overridden_methods.h:61:68: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'. virtual void SomeConstMethodWithExceptionSpec() const throw(int) {} - ^ - override -./overridden_methods.h:63:39: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'. + ^ + override +./overridden_methods.h:63:40: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'. virtual void SomeNonPureBaseMethod() {} - ^ - override + ^ + override ./overridden_methods.h:65:39: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'. virtual void SomeMethodWithComment(); // This is a comment. ^ override -./overridden_methods.h:67:46: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'. +./overridden_methods.h:67:47: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'. virtual void SomeMethodWithCommentAndBody() {} // This is a comment. - ^ - override -overridden_methods.cpp:15:28: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'. + ^ + override +overridden_methods.cpp:15:29: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'. virtual void SomeMethod() = 0; - ^ - override -overridden_methods.cpp:22:40: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'. + ^ + override +overridden_methods.cpp:22:41: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'. virtual ~ImplementationDerivedClass() {} - ^ - override + ^ + override overridden_methods.cpp:24:28: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'. virtual void SomeMethod(); ^ override -overridden_methods.cpp:28:34: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'. +overridden_methods.cpp:28:35: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'. virtual void SomeInlineMethod() {} - ^ - override -overridden_methods.cpp:32:39: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'. + ^ + override +overridden_methods.cpp:32:40: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'. virtual void SomeConstMethod() const {} - ^ - override -overridden_methods.cpp:34:55: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'. + ^ + override +overridden_methods.cpp:34:54: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'. virtual void SomeMethodWithExceptionSpec() throw() {} - ^ - override -overridden_methods.cpp:37:69: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'. + ^ + override +overridden_methods.cpp:37:68: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'. virtual void SomeConstMethodWithExceptionSpec() const throw(int) {} - ^ - override -overridden_methods.cpp:39:39: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'. + ^ + override +overridden_methods.cpp:39:40: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'. virtual void SomeNonPureBaseMethod() {} - ^ - override + ^ + override overridden_methods.cpp:41:39: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'. virtual void SomeMethodWithComment(); // This is a comment. ^ override -overridden_methods.cpp:43:46: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'. +overridden_methods.cpp:43:47: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'. virtual void SomeMethodWithCommentAndBody() {} // This is a comment. - ^ - override + ^ + override 20 warnings generated.
diff --git a/tools/clang/plugins/tests/system/windows.h b/tools/clang/plugins/tests/system/windows.h new file mode 100644 index 0000000..f93d5e2 --- /dev/null +++ b/tools/clang/plugins/tests/system/windows.h
@@ -0,0 +1,10 @@ +// 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 TOOLS_CLANG_PLUGINS_TESTS_SYSTEM_WINDOWS_H_ +#define TOOLS_CLANG_PLUGINS_TESTS_SYSTEM_WINDOWS_H_ + +#define STDMETHOD(x) virtual void x + +#endif // TOOLS_CLANG_PLUGINS_TESTS_SYSTEM_WINDOWS_H_
diff --git a/tools/clang/plugins/tests/test.sh b/tools/clang/plugins/tests/test.sh index ea210544..cf26252 100755 --- a/tools/clang/plugins/tests/test.sh +++ b/tools/clang/plugins/tests/test.sh
@@ -12,6 +12,8 @@ failed_any_test= +THIS_DIR="$(dirname "${0}")" + # Prints usage information. usage() { echo "Usage: $(basename "${0}")" \ @@ -38,6 +40,7 @@ local output="$("${CLANG_PATH}" -fsyntax-only -Wno-c++11-extensions \ -Wno-inconsistent-missing-override \ + -isystem ${THIS_DIR}/system \ -Xclang -load -Xclang "${PLUGIN_PATH}" \ -Xclang -add-plugin -Xclang find-bad-constructs ${flags} ${1} 2>&1)" local diffout="$(echo "${output}" | diff - "${2}")" @@ -81,7 +84,7 @@ # The golden files assume that the cwd is this directory. To make the script # work no matter what the cwd is, explicitly cd to there. - cd "$(dirname "${0}")" + cd "${THIS_DIR}" fi for input in *.cpp; do
diff --git a/tools/clang/plugins/tests/virtual_specifiers.cpp b/tools/clang/plugins/tests/virtual_specifiers.cpp index 28321e8..2103248 100644 --- a/tools/clang/plugins/tests/virtual_specifiers.cpp +++ b/tools/clang/plugins/tests/virtual_specifiers.cpp
@@ -5,6 +5,8 @@ // Tests for chromium style checks for virtual/override/final specifiers on // virtual methods. +#include <windows.h> + // Purposely use macros to test that the FixIt hints don't try to remove the // macro body. #define OVERRIDE override @@ -79,7 +81,13 @@ virtual void F() override = 0; }; -// Finally, some simple sanity tests that overrides in the testing namespace +// Test that the redundant virtual warning is suppressed when the virtual +// keyword comes from a macro in a system header. +class COMIsAwesome : public Base { + STDMETHOD(F)() override = 0; +}; + +// Some tests that overrides in the testing namespace // don't trigger warnings, except for testing::Test. namespace testing {
diff --git a/tools/clang/plugins/tests/virtual_specifiers.txt b/tools/clang/plugins/tests/virtual_specifiers.txt index a51236c..135ebdd3 100644 --- a/tools/clang/plugins/tests/virtual_specifiers.txt +++ b/tools/clang/plugins/tests/virtual_specifiers.txt
@@ -1,69 +1,69 @@ -virtual_specifiers.cpp:36:21: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'. +virtual_specifiers.cpp:38:22: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'. ~MissingOverride() {} - ^ - override -virtual_specifiers.cpp:37:11: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'. + ^ + override +virtual_specifiers.cpp:39:12: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'. void F() {} - ^ - override -virtual_specifiers.cpp:43:3: warning: [chromium-style] 'virtual' is redundant; 'override' implies 'virtual'. + ^ + override +virtual_specifiers.cpp:45:3: warning: [chromium-style] 'virtual' is redundant; 'override' implies 'virtual'. virtual ~VirtualAndOverride() OVERRIDE {} ^~~~~~~~ -virtual_specifiers.cpp:44:3: warning: [chromium-style] 'virtual' is redundant; 'override' implies 'virtual'. +virtual_specifiers.cpp:46:3: warning: [chromium-style] 'virtual' is redundant; 'override' implies 'virtual'. virtual void F() OVERRIDE {} ^~~~~~~~ -virtual_specifiers.cpp:49:3: warning: [chromium-style] 'virtual' is redundant; 'final' implies 'virtual'. +virtual_specifiers.cpp:51:3: warning: [chromium-style] 'virtual' is redundant; 'final' implies 'virtual'. virtual ~VirtualAndFinal() FINAL {} ^~~~~~~~ -virtual_specifiers.cpp:50:3: warning: [chromium-style] 'virtual' is redundant; 'final' implies 'virtual'. +virtual_specifiers.cpp:52:3: warning: [chromium-style] 'virtual' is redundant; 'final' implies 'virtual'. virtual void F() FINAL {} ^~~~~~~~ -virtual_specifiers.cpp:55:3: warning: [chromium-style] 'virtual' is redundant; 'override' implies 'virtual'. +virtual_specifiers.cpp:57:3: warning: [chromium-style] 'virtual' is redundant; 'override' implies 'virtual'. virtual ~VirtualAndOverrideFinal() OVERRIDE FINAL {} ^~~~~~~~ -virtual_specifiers.cpp:55:38: warning: [chromium-style] 'override' is redundant; 'final' implies 'override'. +virtual_specifiers.cpp:57:38: warning: [chromium-style] 'override' is redundant; 'final' implies 'override'. virtual ~VirtualAndOverrideFinal() OVERRIDE FINAL {} ^~~~~~~~~ -virtual_specifiers.cpp:10:18: note: expanded from macro 'OVERRIDE' +virtual_specifiers.cpp:12:18: note: expanded from macro 'OVERRIDE' #define OVERRIDE override ^ -virtual_specifiers.cpp:56:3: warning: [chromium-style] 'virtual' is redundant; 'override' implies 'virtual'. +virtual_specifiers.cpp:58:3: warning: [chromium-style] 'virtual' is redundant; 'override' implies 'virtual'. virtual void F() OVERRIDE FINAL {} ^~~~~~~~ -virtual_specifiers.cpp:56:20: warning: [chromium-style] 'override' is redundant; 'final' implies 'override'. +virtual_specifiers.cpp:58:20: warning: [chromium-style] 'override' is redundant; 'final' implies 'override'. virtual void F() OVERRIDE FINAL {} ^~~~~~~~~ -virtual_specifiers.cpp:10:18: note: expanded from macro 'OVERRIDE' +virtual_specifiers.cpp:12:18: note: expanded from macro 'OVERRIDE' #define OVERRIDE override ^ -virtual_specifiers.cpp:61:23: warning: [chromium-style] 'override' is redundant; 'final' implies 'override'. +virtual_specifiers.cpp:63:23: warning: [chromium-style] 'override' is redundant; 'final' implies 'override'. ~OverrideAndFinal() OVERRIDE FINAL {} ^~~~~~~~~ -virtual_specifiers.cpp:10:18: note: expanded from macro 'OVERRIDE' +virtual_specifiers.cpp:12:18: note: expanded from macro 'OVERRIDE' #define OVERRIDE override ^ -virtual_specifiers.cpp:62:12: warning: [chromium-style] 'override' is redundant; 'final' implies 'override'. +virtual_specifiers.cpp:64:12: warning: [chromium-style] 'override' is redundant; 'final' implies 'override'. void F() OVERRIDE FINAL {} ^~~~~~~~~ -virtual_specifiers.cpp:10:18: note: expanded from macro 'OVERRIDE' +virtual_specifiers.cpp:12:18: note: expanded from macro 'OVERRIDE' #define OVERRIDE override ^ -virtual_specifiers.cpp:67:19: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'. +virtual_specifiers.cpp:69:20: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'. virtual void F() = 0; - ^ - override -virtual_specifiers.cpp:71:11: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'. + ^ + override +virtual_specifiers.cpp:73:12: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'. void F() = 0; - ^ - override -virtual_specifiers.cpp:79:3: warning: [chromium-style] 'virtual' is redundant; 'override' implies 'virtual'. + ^ + override +virtual_specifiers.cpp:81:3: warning: [chromium-style] 'virtual' is redundant; 'override' implies 'virtual'. virtual void F() override = 0; ^~~~~~~~ -virtual_specifiers.cpp:102:20: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'. +virtual_specifiers.cpp:110:20: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'. virtual ~MyTest(); ^ override -virtual_specifiers.cpp:103:3: warning: [chromium-style] 'virtual' is redundant; 'override' implies 'virtual'. +virtual_specifiers.cpp:111:3: warning: [chromium-style] 'virtual' is redundant; 'override' implies 'virtual'. virtual void SetUp() override; ^~~~~~~~ 17 warnings generated.
diff --git a/tools/clang/scripts/update.sh b/tools/clang/scripts/update.sh index 1348341c..f0dbeb0 100755 --- a/tools/clang/scripts/update.sh +++ b/tools/clang/scripts/update.sh
@@ -11,7 +11,7 @@ CLANG_REVISION=233105 # This is incremented when pushing a new build of Clang at the same revision. -CLANG_SUB_REVISION=1 +CLANG_SUB_REVISION=2 PACKAGE_VERSION="${CLANG_REVISION}-${CLANG_SUB_REVISION}" @@ -314,6 +314,7 @@ "${CLANG_DIR}/test/SemaCXX/typo-correction-delayed.cpp" \ "${COMPILER_RT_DIR}/lib/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc" \ "${COMPILER_RT_DIR}/test/tsan/signal_segv_handler.cc" \ + "${COMPILER_RT_DIR}/lib/sanitizer_common/sanitizer_coverage_libcdep.cc" \ ; do if [[ -e "${i}" ]]; then rm -f "${i}" # For unversioned files. @@ -396,6 +397,27 @@ patch -p0 popd + # Cherry-pick r234010 [sancov] Shrink pc array on Android back to 2**24." + pushd "${COMPILER_RT_DIR}" + cat << 'EOF' | +diff --git a/lib/sanitizer_common/sanitizer_coverage_libcdep.cc b/lib/sanitizer_common/sanitizer_coverage_libcdep.cc +index 4b976fc..cfd9e7e 100644 +--- a/lib/sanitizer_common/sanitizer_coverage_libcdep.cc ++++ b/lib/sanitizer_common/sanitizer_coverage_libcdep.cc +@@ -109,7 +109,8 @@ class CoverageData { + + // Maximal size pc array may ever grow. + // We MmapNoReserve this space to ensure that the array is contiguous. +- static const uptr kPcArrayMaxSize = FIRST_32_SECOND_64(1 << 26, 1 << 27); ++ static const uptr kPcArrayMaxSize = ++ FIRST_32_SECOND_64(1 << (SANITIZER_ANDROID ? 24 : 26), 1 << 27); + // The amount file mapping for the pc array is grown by. + static const uptr kPcArrayMmapSize = 64 * 1024; + +EOF + patch -p1 + popd + # This Go bindings test doesn't work after the bootstrap build on Linux. (PR21552) pushd "${LLVM_DIR}" cat << 'EOF' | @@ -543,6 +565,14 @@ BINUTILS_INCDIR="${ABS_BINUTILS_DIR}/Linux_x64/Release/include" fi + +# If building at head, define a macro that plugins can use for #ifdefing +# out code that builds at head, but not at CLANG_REVISION or vice versa. +if [[ -n ${LLVM_FORCE_HEAD_REVISION:-''} ]]; then + CFLAGS="${CFLAGS} -DLLVM_FORCE_HEAD_REVISION" + CXXFLAGS="${CXXFLAGS} -DLLVM_FORCE_HEAD_REVISION" +fi + # Hook the Chromium tools into the LLVM build. Several Chromium tools have # dependencies on LLVM/Clang libraries. The LLVM build detects implicit tools # in the tools subdirectory, so install a shim CMakeLists.txt that forwards to
diff --git a/tools/gn/command_help.cc b/tools/gn/command_help.cc index fda82696..7679b17 100644 --- a/tools/gn/command_help.cc +++ b/tools/gn/command_help.cc
@@ -92,7 +92,11 @@ const base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess(); if (cmdline->HasSwitch(switches::kMarkdown)) { OutputString("# GN Reference\n\n"); - OutputString("[TOC]\n\n"); + + // TODO: https://code.google.com/p/gitiles/issues/detail?id=75 + // Gitiles crashes when rendering the table of contents, so we must omit + // it until the bug is fixed. + // OutputString("[TOC]\n\n"); OutputString("*This page is automatically generated from* " "`gn help --markdown all`.\n\n"); } else {
diff --git a/tools/gn/docs/reference.md b/tools/gn/docs/reference.md index 39550a2..a405a34 100644 --- a/tools/gn/docs/reference.md +++ b/tools/gn/docs/reference.md
@@ -1,7 +1,5 @@ # GN Reference -[TOC] - *This page is automatically generated from* `gn help --markdown all`. ## **--args**: Specifies build arguments overrides. @@ -565,7 +563,7 @@ ``` -## **gn refs <out_dir> (<label_pattern>|<label>|<file>)* [--all]** +## **gn refs <out_dir> (<label_pattern>|<label>|<file>|@<response_file>)* [--all]** ``` [--all-toolchains] [--as=...] [--testonly=...] [--type=...] @@ -587,6 +585,11 @@ that does not contain wildcards and does not match a target or a config will be treated as a file. + - Response file: If the input starts with an "@", it will be + interpreted as a path to a file containing a list of labels or + file names, one per line. This allows us to handle long lists + of inputs without worrying about command line limits. + ``` ### **Options**
diff --git a/tools/gn/misc/GN.tmLanguage b/tools/gn/misc/GN.tmLanguage new file mode 100644 index 0000000..8a0f8a2 --- /dev/null +++ b/tools/gn/misc/GN.tmLanguage
@@ -0,0 +1,102 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>fileTypes</key> + <array> + <string>gn</string> + <string>gni</string> + </array> + <key>name</key> + <string>GN</string> + <key>patterns</key> + <array> + <dict> + <key>comment</key> + <string>keywords</string> + <key>match</key> + <string>\b(?:if)\b</string> + <key>name</key> + <string>keyword.control.gn</string> + </dict> + <dict> + <key>comment</key> + <string>constants</string> + <key>match</key> + <string>\b(?:true|false)\b</string> + <key>name</key> + <string>constant.language.gn</string> + </dict> + <dict> + <key>comment</key> + <string>numbers</string> + <key>match</key> + <string>\b\d+\.?(?:\d+)?\b</string> + <key>name</key> + <string>constant.numeric.gn</string> + </dict> + <dict> + <key>comment</key> + <string>double quoted string</string> + <key>match</key> + <string>\"[^\"]*\"</string> + <key>name</key> + <string>string.quoted.double.gn</string> + </dict> + <dict> + <key>comment</key> + <string>comment</string> + <key>begin</key> + <string>#</string> + <key>end</key> + <string>$</string> + <key>name</key> + <string>comment.gn</string> + </dict> + <dict> + <key>comment</key> + <string>operators</string> + <key>match</key> + <string>(?:=|==|\+=|-=|\+|-)</string> + <key>name</key> + <string>keyword.operator.gn</string> + </dict> + <dict> + <key>comment</key> + <string>targets</string> + <key>match</key> + <string>\b(?:action|action_foreach|copy|executable|group|shared_library|source_set|static_library)\b</string> + <key>name</key> + <string>entity.name.tag.gn</string> + </dict> + <dict> + <key>comment</key> + <string>functions</string> + <key>match</key> + <string>\b(?:assert|config|declare_args|defined|exec_script|foreach|get_label_info|get_path_info|get_target_outputs|getenv|import|print|process_file_template|read_file|rebase_path|set_default_toolchain|set_defaults|set_sources_assignment_filter|template|tool|toolchain|toolchain_args|write_file)\b</string> + <key>name</key> + <string>entity.name.function.gn</string> + </dict> + <dict> + <key>comment</key> + <string>predefined variables</string> + <key>match</key> + <string>\b(?:current_cpu|current_os|current_toolchain|default_toolchain|host_cpu|host_os|python_path|root_build_dir|root_gen_dir|root_out_dir|target_cpu|target_gen_dir|target_os|target_out_dir)\b</string> + <key>name</key> + <string>variable.parameter.gn</string> + </dict> + <dict> + <key>comment</key> + <string>target variables</string> + <key>match</key> + <string>\b(?:all_dependent_configs|allow_circular_includes_from|args|cflags|cflags_c|cflags_cc|cflags_objc|cflags_objcc|check_includes|complete_static_lib|configs|data|data_deps|defines|depfile|deps|forward_dependent_configs_from|include_dirs|inputs|ldflags|lib_dirs|libs|output_extension|output_name|outputs|public|public_configs|public_deps|script|sources|testonly|visibility)\b</string> + <key>name</key> + <string>entity.other.attribute-name.gn</string> + </dict> + </array> + <key>scopeName</key> + <string>source.gn</string> + <key>uuid</key> + <string>DE419F8C-EC46-4824-87F3-732BD08694DC</string> +</dict> +</plist>
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl index 9fab045..7fa3014 100644 --- a/tools/mb/mb_config.pyl +++ b/tools/mb/mb_config.pyl
@@ -162,6 +162,10 @@ 'Linux GN': 'gn_release_bot', 'Linux GN (dbg)': 'gn_debug_bot' }, + 'client.v8': { + 'V8 Linux GN': 'gn_release_bot', + 'V8 Android GN (dbg)': 'android_gn_debug_bot', + }, 'tryserver.blink': { 'android_chromium_gn_compile_rel': 'android_gn_release_bot', 'linux_chromium_gn_rel': 'gn_release_bot', @@ -188,5 +192,9 @@ 'win8_chromium_gn_rel': 'gn_release_trybot_x86', 'win8_chromium_gn_upload': 'gn_release_bot', }, + 'tryserver.v8': { + 'v8_linux_chromium_gn_rel': 'gn_release_trybot', + 'v8_android_chromium_gn_dbg': 'android_gn_debug_bot', + }, }, }
diff --git a/tools/metrics/actions/actions.xml b/tools/metrics/actions/actions.xml index 5f98d958b..327f44d 100644 --- a/tools/metrics/actions/actions.xml +++ b/tools/metrics/actions/actions.xml
@@ -7091,6 +7091,14 @@ </description> </action> +<action name="Launcher_SwitchTask"> + <owner>bruthig@google.com</owner> + <owner>tdanderson@google.com</owner> + <description> + Recorded when the user activates an existing task from the shelf. + </description> +</action> + <action name="LoadURL"> <owner>Please list the metric's owners. Add more owner tags as needed.</owner> <description>Please enter the description of this user action.</description> @@ -8469,6 +8477,20 @@ <description>Settings: Accessibility: Enable tap dragging</description> </action> +<action name="Options_AddPersonEnabled_Disable"> + <owner>jwd@chromium.org</owner> + <description> + Settings: People: Disables allowing anyone to add a person to Chrome. + </description> +</action> + +<action name="Options_AddPersonEnabled_Enable"> + <owner>jwd@chromium.org</owner> + <description> + Settings: People: Enables anyone to add a person to Chrome. + </description> +</action> + <action name="Options_AllowAllUsers_Disable"> <owner>stevenjb@chromium.org</owner> <description> @@ -8601,6 +8623,16 @@ <description>Settings: Bluetooth: Add a device</description> </action> +<action name="Options_BrowserGuestEnabled_Disable"> + <owner>jwd@chromium.org</owner> + <description>Settings: People: Disable Guest browsing.</description> +</action> + +<action name="Options_BrowserGuestEnabled_Enable"> + <owner>jwd@chromium.org</owner> + <description>Settings: People: Allows guests to browse.</description> +</action> + <action name="Options_CaptivePortalBypassProxy_Disable"> <owner>alemate@chromium.org</owner> <description> @@ -8881,6 +8913,11 @@ <description>Please enter the description of this user action.</description> </action> +<action name="Options_GetMoreExtensions"> + <owner>jwd@chromium.org</owner> + <description>Extensions: Click on 'Get More Extensions' link.</description> +</action> + <action name="Options_GoogleGeolocationAccessCheckbox_Disable"> <owner>Please list the metric's owners. Add more owner tags as needed.</owner> <description>Please enter the description of this user action.</description> @@ -9128,6 +9165,11 @@ <description>Please enter the description of this user action.</description> </action> +<action name="Options_LoadUnpackedExtension"> + <owner>jwd@chromium.org</owner> + <description>Extensions: Loads an unpacked extension.</description> +</action> + <action name="Options_ManageAccounts"> <owner>stevenjb@chromium.org</owner> <description>Settings: Users: Manage other users</description> @@ -9710,6 +9752,20 @@ <description>Settings: Date and time: Timezone</description> </action> +<action name="Options_ToggleDeveloperMode_Disabled"> + <owner>jwd@chromium.org</owner> + <description> + Extensions: Disables developer mode for Chrome extensions. + </description> +</action> + +<action name="Options_ToggleDeveloperMode_Enabled"> + <owner>jwd@chromium.org</owner> + <description> + Extensions: Enables developer mode for Chrome extensions. + </description> +</action> + <action name="Options_TouchpadNaturalScroll_Disable"> <owner>Please list the metric's owners. Add more owner tags as needed.</owner> <description>Please enter the description of this user action.</description> @@ -9740,6 +9796,11 @@ <description>Please enter the description of this user action.</description> </action> +<action name="Options_UpdateExtensions"> + <owner>jwd@chromium.org</owner> + <description>Extensions: Click on update extensions in settings.</description> +</action> + <action name="Options_Use24HourClockCheckbox_Disable"> <owner>michaelpg@chromium.org</owner> <description>Settings: Date and time: Use 24-hour clock</description>
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml index 14f0abb..7ffa0ec 100644 --- a/tools/metrics/histograms/histograms.xml +++ b/tools/metrics/histograms/histograms.xml
@@ -11394,6 +11394,15 @@ </summary> </histogram> +<histogram name="GPU.ANGLE.D3D11CreateDeviceMS" units="milliseconds"> + <owner>jmadill@chromium.org</owner> + <summary> + The time that elapses for the initial call to D3D11CreateDevice on D3D11 + ANGLE. A pure system call, with no ANGLE or Chromium code, called once every + GPU process startup. + </summary> +</histogram> + <histogram name="GPU.ANGLE.D3D11InitializeResult" enum="D3D11InitializeResult"> <owner>jmadill@chromium.org</owner> <summary> @@ -13087,6 +13096,14 @@ </summary> </histogram> +<histogram name="interstitial.ssl.good_cert_seen" enum="SSLGoodCertSeenEvent"> + <owner>jww@chromium.org</owner> + <summary> + Emitted when a good certificate is seen, specifying whether the user already + gave an exception for a bad certificate for the same host. + </summary> +</histogram> + <histogram name="interstitial.ssl.severity_score.authority_invalid" units="%"> <obsolete> Deprecated Jan 2015 (M42). @@ -13194,6 +13211,57 @@ </summary> </histogram> +<histogram name="Layout.MicroSecondsPerComplexText" units="microseconds"> + <owner>benjhayden@chromium.org</owner> + <summary> + For layouts that considered at least 100 nodes, at least half of which were + complex text fragments, the average number of microseconds spent laying out + each node. + </summary> +</histogram> + +<histogram name="Layout.MicroSecondsPerFloat" units="microseconds"> + <owner>benjhayden@chromium.org</owner> + <summary> + For layouts that considered at least 100 nodes, at least half of which were + floats, the average number of microseconds spent laying out each node. + </summary> +</histogram> + +<histogram name="Layout.MicroSecondsPerNode" units="microseconds"> + <owner>benjhayden@chromium.org</owner> + <summary> + For layouts that considered at least 100 nodes, the average number of + microseconds spent laying out each node. + </summary> +</histogram> + +<histogram name="Layout.MicroSecondsPerPositioned" units="microseconds"> + <owner>benjhayden@chromium.org</owner> + <summary> + For layouts that considered at least 100 nodes, at least half of which were + fixed or absolute positioned, the average number of microseconds spent + laying out each node. + </summary> +</histogram> + +<histogram name="Layout.MicroSecondsPerSimpleText" units="microseconds"> + <owner>benjhayden@chromium.org</owner> + <summary> + For layouts that considered at least 100 nodes, at least half of which were + simple text fragments, the average number of microseconds spent laying out + each node. + </summary> +</histogram> + +<histogram name="Layout.MicroSecondsPerTD" units="microseconds"> + <owner>benjhayden@chromium.org</owner> + <summary> + For layouts that considered at least 100 nodes, at least half of which were + table cells, the average number of microseconds spent laying out each node. + </summary> +</histogram> + <histogram name="LevelDBEnv.All.SafeThreadAccess" units="accesses"> <obsolete> Deprecated 2013-10. No thread-unsafety was found. @@ -13639,6 +13707,12 @@ </summary> </histogram> +<histogram name="Login.StateKeyGenerationStatus" + enum="LoginStateKeyGenerationStatus"> + <owner>mnissler@chromium.org</owner> + <summary>Result of a state key generation operation.</summary> +</histogram> + <histogram name="Login.SuccessReason" enum="LoginSuccessReason"> <owner>nkostylev@chromium.org</owner> <summary>Chrome OS login success reason.</summary> @@ -25450,6 +25524,81 @@ </summary> </histogram> +<histogram name="PasswordManager.AffiliationBackend.FetchSize" units="facets"> + <owner>engedy@chromium.org</owner> + <summary> + The number of facets for which affiliation information was requested in a + network fetch. Recorded for each network fetch. + </summary> +</histogram> + +<histogram name="PasswordManager.AffiliationBackend.FirstFetchDelay" + units="milliseconds"> + <owner>engedy@chromium.org</owner> + <summary> + The time elapsed between creation of the AffiliationBackend and the first + time it needed to issue a network fetch. + </summary> +</histogram> + +<histogram name="PasswordManager.AffiliationBackend.SubsequentFetchDelay" + units="milliseconds"> + <owner>engedy@chromium.org</owner> + <summary> + The elapsed time between subsequent network fetches. Recorded whenever the + AffiliationBackend initiated a network fetch, regardless of success or + failure. + </summary> +</histogram> + +<histogram name="PasswordManager.AffiliationDummyData.RequestResultCount" + units="results"> + <owner>engedy@chromium.org</owner> + <summary> + The number of facets affiliated with a dummy Web facet, according to the + affiliation information retrieved from the cache. Recorded for each dummy + Web facet, once shortly after start-up, and then periodically every hour; + but only if getting affiliations succeeded for the Web facet. + </summary> +</histogram> + +<histogram name="PasswordManager.AffiliationDummyData.RequestSuccess" + enum="BooleanSuccess"> + <owner>engedy@chromium.org</owner> + <summary> + Whether or not affiliations of a dummy Web facet could be successfully + retrieved from the cache. Recorded for each dummy Web facet, once shortly + after start-up, and then periodically every hour. + </summary> +</histogram> + +<histogram name="PasswordManager.AffiliationFetcher.FetchErrorCode" + enum="NetErrorCodes"> + <owner>engedy@chromium.org</owner> + <summary> + The network error code, as reported by the underlying URLFetcher. Recorded + only for each network fetch that failed due to network/server errors. + </summary> +</histogram> + +<histogram name="PasswordManager.AffiliationFetcher.FetchHttpResponseCode" + enum="HttpResponseCode"> + <owner>engedy@chromium.org</owner> + <summary> + The HTTP response code, as reported by the underlying URLFetcher. Recorded + only for each network fetch that failed due to network/server errors. + </summary> +</histogram> + +<histogram name="PasswordManager.AffiliationFetcher.FetchResult" + enum="AffiliationFetchResult"> + <owner>engedy@chromium.org</owner> + <summary> + Whether the network fetch succeeded, failed due to network/server errors, or + contained malformed data. Recorded for each network fetch. + </summary> +</histogram> + <histogram name="PasswordManager.AllowToCollectURLBubble.UIDismissalReason" enum="PasswordManagerAllowToCollectURLBubble.UIDismissalReason"> <obsolete> @@ -44305,6 +44454,49 @@ </summary> </histogram> +<histogram name="WebCore.IndexedDB.Schema.Index.KeyPathType" + enum="IDBKeyPathType"> + <owner>jsbell@chromium.org</owner> + <summary> + Records the 'keyPath' type (none, string, or array) during IDBObjectStore's + createIndex operation. See http://www.w3.org/TR/IndexedDB/ + </summary> +</histogram> + +<histogram name="WebCore.IndexedDB.Schema.Index.MultiEntry" enum="Boolean"> + <owner>jsbell@chromium.org</owner> + <summary> + Records the 'multiEntry' flag value during IDBObjectStore's createIndex + operation. See http://www.w3.org/TR/IndexedDB/ + </summary> +</histogram> + +<histogram name="WebCore.IndexedDB.Schema.Index.Unique" enum="Boolean"> + <owner>jsbell@chromium.org</owner> + <summary> + Records the 'unique' flag value during IDBObjectStore's createIndex + operation. See http://www.w3.org/TR/IndexedDB/ + </summary> +</histogram> + +<histogram name="WebCore.IndexedDB.Schema.ObjectStore.AutoIncrement" + enum="Boolean"> + <owner>jsbell@chromium.org</owner> + <summary> + Records the 'autoIncrement' flag value during IDBDatabase's + createObjectStore operation. See http://www.w3.org/TR/IndexedDB/ + </summary> +</histogram> + +<histogram name="WebCore.IndexedDB.Schema.ObjectStore.KeyPathType" + enum="IDBKeyPathType"> + <owner>jsbell@chromium.org</owner> + <summary> + Records the 'keyPath' type (none, string, or array) during IDBDatabase's + createObjectStore operation. See http://www.w3.org/TR/IndexedDB/ + </summary> +</histogram> + <histogram name="WebCore.PreloadDelayMs" units="milliseconds"> <owner>Please list the metric's owners. Add more owner tags as needed.</owner> <summary> @@ -45890,6 +46082,12 @@ <int value="2" label="IPv6"/> </enum> +<enum name="AffiliationFetchResult" type="int"> + <int value="0" label="Success"/> + <int value="1" label="Network/server error"/> + <int value="2" label="Malformed response"/> +</enum> + <enum name="AlternateProtocolUsage" type="int"> <int value="0" label="ALTERNATE_PROTOCOL_USAGE_NO_RACE"/> <int value="1" label="ALTERNATE_PROTOCOL_USAGE_WON_RACE"/> @@ -48425,6 +48623,7 @@ <int value="295718620" label="cespy.dll"/> <int value="313484566" label="vntsrv.dll"/> <int value="447643466" label="libinject.dll"/> + <int value="604217493" label="explorerex.dll"/> <int value="685821492" label="smdmf.dll"/> <int value="750761702" label="systemk.dll"/> <int value="777975221" label="activedetect32.dll"/> @@ -52138,6 +52337,7 @@ label="V8HTMLTextAreaElement_Autocapitalize_AttributeSetter"/> <int value="758" label="SVGHrefBaseVal"/> <int value="759" label="SVGHrefAnimVal"/> + <int value="760" label="AutocapitalizeAttribute"/> </enum> <enum name="FFmpegCodecs" type="int"> @@ -53317,6 +53517,12 @@ </int> </enum> +<enum name="IDBKeyPathType" type="int"> + <int value="0" label="None">No key path.</int> + <int value="1" label="String">Key path is a string.</int> + <int value="2" label="Array">Key path is an array of strings.</int> +</enum> + <enum name="IDBLevelDBBackingStoreInternalErrorType" type="int"> <int value="0" label="IDBLevelDBBackingStoreReadError"> IndexedDB encountered an error attempting to read or decode a value from the @@ -54948,6 +55154,7 @@ <int value="-820041355" label="enable-transition-compositing"/> <int value="-814097014" label="disable-session-crashed-bubble"/> <int value="-813474479" label="site-per-process"/> + <int value="-802348444" label="disable-site-engagement-service"/> <int value="-795600188" label="disable-async-dns"/> <int value="-780798969" label="disable-single-click-autofill"/> <int value="-770319039" label="enable-touch-editing"/> @@ -54971,6 +55178,7 @@ <int value="-562274241" label="enable-extension-action-redesign"/> <int value="-560551550" label="use-memory-pressure-chromeos"/> <int value="-536289234" label="ssl-interstitial-v2-colorful"/> + <int value="-519960638" label="enable-site-engagement-service"/> <int value="-516845951" label="enable-embedded-extension-options"/> <int value="-512971943" label="disable-one-copy"/> <int value="-510488450" label="disable-pnacl"/> @@ -55073,6 +55281,8 @@ <int value="625273056" label="disable-boot-animation"/> <int value="630947363" label="touch-events"/> <int value="643725031" label="disable-touch-feedback"/> + <int value="683410401" + label="enable-proximity-auth-bluetooth-low-energy-discovery"/> <int value="689489984" label="disable-zero-suggest"/> <int value="709850261" label="disable-touch-editing"/> <int value="711424932" label="enable-cloud-print-xps"/> @@ -55176,6 +55386,7 @@ <int value="1505194447" label="disable-transition-compositing"/> <int value="1510476448" label="disable-prefixed-encrypted-media"/> <int value="1515196403" label="fast-user-switching"/> + <int value="1560188739" label="reader-mode-heuristics"/> <int value="1589341623" label="disable-easy-unlock"/> <int value="1612974229" label="allow-insecure-localhost"/> <int value="1622131033" label="ozone-test-single-overlay-support"/> @@ -55306,6 +55517,24 @@ <int value="43" label="RESERVED">Reserved</int> </enum> +<enum name="LoginStateKeyGenerationStatus" type="int"> + <summary>The result of a state key generation operation.</summary> + <int value="0" label="GENERATION_METHOD_IDENTIFIER_HASH"> + Successfully generated state keys from machine identifiers. + </int> + <int value="1" label="GENERATION_METHOD_HMAC_DEVICE_SECRET"> + Successfully generated state keys from stable device secret. + </int> + <int value="2" label="MISSING_IDENTIFIERS"> + Failed due to missing machine IDs. + </int> + <int value="3" label="BAD_DEVICE_SECRET"> + Failed due to invalid device secret input. + </int> + <int value="4" label="HMAC_INIT_FAILURE">HMAC initialization failed.</int> + <int value="5" label="HMAC_SIGN_FAILURE">HMAC computation failed.</int> +</enum> + <enum name="LoginSuccessReason" type="int"> <int value="0" label="OFFLINE_AND_ONLINE"> Login success offline and online @@ -62335,6 +62564,11 @@ <int value="9" label="UNKNOWN"/> </enum> +<enum name="SSLGoodCertSeenEvent" type="int"> + <int value="0" label="NO_PREVIOUS_EXCEPTION"/> + <int value="1" label="HAD_PREVIOUS_EXCEPTION"/> +</enum> + <enum name="SSLIsExpiredAndDecision" type="int"> <int value="0" label="EXPIRED_AND_PROCEED"/> <int value="1" label="EXPIRED_AND_DO_NOT_PROCEED"/> @@ -62359,6 +62593,7 @@ <int value="102" label="ALPN, SPDY 3.1"/> <int value="103" label="ALPN, HTTP/2 draft-14"/> <int value="104" label="ALPN, HTTP/2 draft-15"/> + <int value="107" label="ALPN, HTTP/2"/> <int value="200" label="ALPN, QUIC/1 + SPDY/3"/> <int value="501" label="NPN, HTTP/1.1"/> <int value="600" label="NPN, SPDY 2.0"/> @@ -62366,6 +62601,7 @@ <int value="602" label="NPN, SPDY 3.1"/> <int value="603" label="NPN, HTTP/2 draft-14"/> <int value="604" label="NPN, HTTP/2 draft-15"/> + <int value="607" label="NPN, HTTP/2"/> <int value="700" label="NPN, QUIC/1 + SPDY/3"/> <int value="1001" label="NPN, fallback to HTTP/1.1"/> <int value="1100" label="NPN, fallback to SPDY 2.0"/> @@ -62373,6 +62609,7 @@ <int value="1102" label="NPN, fallback to SPDY 3.1"/> <int value="1103" label="NPN, fallback to HTTP/2 draft-14"/> <int value="1104" label="NPN, fallback to HTTP/2 draft-15"/> + <int value="1107" label="NPN, fallback to HTTP/2"/> <int value="1200" label="NPN, fallback to QUIC/1 + SPDY/3"/> </enum> @@ -64077,6 +64314,17 @@ <affected-histogram name="CaptivePortal.Session.DiscrepancyWithShill"/> </histogram_suffixes> +<histogram_suffixes name="AffiliationDummyData" separator="."> + <suffix name="OnStartup" + label="with the dummy data being requested shortly after start-up"/> + <suffix name="Periodic" + label="with the dummy data being requested periodically later"/> + <affected-histogram + name="PasswordManager.AffiliationDummyData.RequestResultCount"/> + <affected-histogram + name="PasswordManager.AffiliationDummyData.RequestSuccess"/> +</histogram_suffixes> + <histogram_suffixes name="AlternateProtocol"> <suffix name="AlternateProtocol_spdy" label="with alternate protocol available but http is used"/> @@ -64534,7 +64782,47 @@ <histogram_suffixes name="DataReductionProxy_TamperingTotal" separator="_"> <suffix name="Total" label="total number of tamperings detected"/> <affected-histogram name="DataReductionProxy.HeaderTamperDetectionHTTP"/> + <affected-histogram name="DataReductionProxy.HeaderTamperDetectionHTTP_CSS"/> + <affected-histogram + name="DataReductionProxy.HeaderTamperDetectionHTTP_Image"/> + <affected-histogram + name="DataReductionProxy.HeaderTamperDetectionHTTP_Image_0_10KB"/> + <affected-histogram + name="DataReductionProxy.HeaderTamperDetectionHTTP_Image_100_500KB"/> + <affected-histogram + name="DataReductionProxy.HeaderTamperDetectionHTTP_Image_10_100KB"/> + <affected-histogram + name="DataReductionProxy.HeaderTamperDetectionHTTP_Image_500KB"/> + <affected-histogram + name="DataReductionProxy.HeaderTamperDetectionHTTP_Image_GIF"/> + <affected-histogram + name="DataReductionProxy.HeaderTamperDetectionHTTP_Image_JPG"/> + <affected-histogram + name="DataReductionProxy.HeaderTamperDetectionHTTP_Image_PNG"/> + <affected-histogram + name="DataReductionProxy.HeaderTamperDetectionHTTP_Image_WEBP"/> + <affected-histogram name="DataReductionProxy.HeaderTamperDetectionHTTP_JS"/> <affected-histogram name="DataReductionProxy.HeaderTamperDetectionHTTPS"/> + <affected-histogram name="DataReductionProxy.HeaderTamperDetectionHTTPS_CSS"/> + <affected-histogram + name="DataReductionProxy.HeaderTamperDetectionHTTPS_Image"/> + <affected-histogram + name="DataReductionProxy.HeaderTamperDetectionHTTPS_Image_0_10KB"/> + <affected-histogram + name="DataReductionProxy.HeaderTamperDetectionHTTPS_Image_100_500KB"/> + <affected-histogram + name="DataReductionProxy.HeaderTamperDetectionHTTPS_Image_10_100KB"/> + <affected-histogram + name="DataReductionProxy.HeaderTamperDetectionHTTPS_Image_500KB"/> + <affected-histogram + name="DataReductionProxy.HeaderTamperDetectionHTTPS_Image_GIF"/> + <affected-histogram + name="DataReductionProxy.HeaderTamperDetectionHTTPS_Image_JPG"/> + <affected-histogram + name="DataReductionProxy.HeaderTamperDetectionHTTPS_Image_PNG"/> + <affected-histogram + name="DataReductionProxy.HeaderTamperDetectionHTTPS_Image_WEBP"/> + <affected-histogram name="DataReductionProxy.HeaderTamperDetectionHTTPS_JS"/> <affected-histogram name="DataReductionProxy.HeaderTamperDetectionPassHTTP"/> <affected-histogram name="DataReductionProxy.HeaderTamperDetectionPassHTTPS"/> <affected-histogram name="DataReductionProxy.HeaderTamperedHTTP_ChromeProxy"/> @@ -64571,6 +64859,22 @@ name="DataReductionProxy.HeaderTamperedHTTPS_Via_Missing"/> </histogram_suffixes> +<histogram_suffixes name="DataReductionProxy_TotalCounts" separator="_"> + <suffix name="JS" label="JavaScript count"/> + <suffix name="CSS" label="CSS count"/> + <suffix name="Image" label="image count"/> + <suffix name="Image_GIF" label="GIF image count"/> + <suffix name="Image_JPG" label="JPG image count"/> + <suffix name="Image_PNG" label="PNG image count"/> + <suffix name="Image_WEBP" label="WEBP image count"/> + <suffix name="Image_0_10KB" label="image counts of 0-10KB"/> + <suffix name="Image_10_100KB" label="image counts of 10-100KB"/> + <suffix name="Image_100_500KB" label="image counts of 100-500KB"/> + <suffix name="Image_500KB" label="image counts of more than 500KB"/> + <affected-histogram name="DataReductionProxy.HeaderTamperDetectionHTTP"/> + <affected-histogram name="DataReductionProxy.HeaderTamperDetectionHTTPS"/> +</histogram_suffixes> + <histogram_suffixes name="DataReductionProxyBypassedBytes" separator="."> <suffix name="SSL" label="Bypass due to SSL"/> <suffix name="LocalBypassRules" @@ -67207,15 +67511,51 @@ <histogram_suffixes name="PrerenderSource" ordering="prefix"> <suffix name="" label="All prerenders."/> - <suffix name="exp1" label="Likelihood threshold experiment 1."/> - <suffix name="exp2" label="Likelihood threshold experiment 2."/> - <suffix name="exp3" label="Likelihood threshold experiment 3."/> - <suffix name="exp4" label="Likelihood threshold experiment 4."/> - <suffix name="exp5" label="Likelihood threshold experiment 5."/> - <suffix name="exp6" label="Likelihood threshold experiment 6."/> - <suffix name="exp7" label="Likelihood threshold experiment 7."/> - <suffix name="exp8" label="Likelihood threshold experiment 8."/> - <suffix name="exp9" label="Likelihood threshold experiment 9."/> + <suffix name="exp1" label="Likelihood threshold experiment 1."> + <obsolete> + Deprecated April 2015 + </obsolete> + </suffix> + <suffix name="exp2" label="Likelihood threshold experiment 2."> + <obsolete> + Deprecated April 2015 + </obsolete> + </suffix> + <suffix name="exp3" label="Likelihood threshold experiment 3."> + <obsolete> + Deprecated April 2015 + </obsolete> + </suffix> + <suffix name="exp4" label="Likelihood threshold experiment 4."> + <obsolete> + Deprecated April 2015 + </obsolete> + </suffix> + <suffix name="exp5" label="Likelihood threshold experiment 5."> + <obsolete> + Deprecated April 2015 + </obsolete> + </suffix> + <suffix name="exp6" label="Likelihood threshold experiment 6."> + <obsolete> + Deprecated April 2015 + </obsolete> + </suffix> + <suffix name="exp7" label="Likelihood threshold experiment 7."> + <obsolete> + Deprecated April 2015 + </obsolete> + </suffix> + <suffix name="exp8" label="Likelihood threshold experiment 8."> + <obsolete> + Deprecated April 2015 + </obsolete> + </suffix> + <suffix name="exp9" label="Likelihood threshold experiment 9."> + <obsolete> + Deprecated April 2015 + </obsolete> + </suffix> <suffix name="gws" label="GWS triggered prerender."/> <suffix name="externalrequest" label="Externally triggered prerender."/> <suffix name="Instant" label="Instant search prerender."/>
diff --git a/tools/metrics/rappor/rappor.xml b/tools/metrics/rappor/rappor.xml index c32d6bd..33502e0 100644 --- a/tools/metrics/rappor/rappor.xml +++ b/tools/metrics/rappor/rappor.xml
@@ -107,6 +107,15 @@ </summary> </rappor-metric> +<rappor-metric name="Autofill.QueryResponseHasNoServerDataForForm" + type="ETLD_PLUS_ONE"> + <owner>mathp@chromium.org</owner> + <summary> + The eTLD+1 of a URL for which there was a server query response for which + the server had no data at all for at least one form. + </summary> +</rappor-metric> + <rappor-metric name="ContentSettings.MixedScript.DisplayedShield" type="ETLD_PLUS_ONE"> <owner>lgarron@chromium.org</owner>
diff --git a/tools/perf/PRESUBMIT.py b/tools/perf/PRESUBMIT.py index 70aadd7..ad11c24 100644 --- a/tools/perf/PRESUBMIT.py +++ b/tools/perf/PRESUBMIT.py
@@ -11,13 +11,6 @@ import os import sys -PYLINT_BLACKLIST = [] -PYLINT_DISABLED_WARNINGS = [ - 'R0923', # Interface not implemented - 'R0201', # Method could be a function - 'E1101', # Non-existent member is accessed. -] - def _CommonChecks(input_api, output_api): """Performs common checks, which includes running pylint.""" @@ -27,9 +20,7 @@ # Modules in tools/perf depend on telemetry. sys.path = [os.path.join(os.pardir, 'telemetry')] + sys.path results.extend(input_api.canned_checks.RunPylint( - input_api, output_api, - black_list=PYLINT_BLACKLIST, - disabled_warnings=PYLINT_DISABLED_WARNINGS)) + input_api, output_api, black_list=[], pylintrc='pylintrc')) results.extend(_CheckJson(input_api, output_api)) results.extend(_CheckWprShaFiles(input_api, output_api)) finally:
diff --git a/tools/perf/benchmarks/blink_style.py b/tools/perf/benchmarks/blink_style.py index cf5049b..0dae4d15 100644 --- a/tools/perf/benchmarks/blink_style.py +++ b/tools/perf/benchmarks/blink_style.py
@@ -34,6 +34,7 @@ return 'blink_style.key_mobile_sites' +@benchmark.Disabled('mac', 'reference') # http://crbug.com/479048 class BlinkStylePolymer(benchmark.Benchmark): """Measures performance of Blink's style engine (CSS Parsing, Style Recalc, etc.) for Polymer cases.
diff --git a/tools/perf/benchmarks/draw_properties.py b/tools/perf/benchmarks/draw_properties.py index 7a985fd..8030af6 100644 --- a/tools/perf/benchmarks/draw_properties.py +++ b/tools/perf/benchmarks/draw_properties.py
@@ -9,8 +9,7 @@ # This benchmark depends on tracing categories available in M43 -@benchmark.Disabled('reference', # http://crbug.com/463111 - 'android') # http://crbug.com/471786 +@benchmark.Disabled('reference') class DrawPropertiesToughScrolling(benchmark.Benchmark): test = draw_properties.DrawProperties page_set = page_sets.ToughScrollingCasesPageSet @@ -20,8 +19,7 @@ # This benchmark depends on tracing categories available in M43 -@benchmark.Disabled('reference','win', # http://crbug.com/463111 - 'android') # http://crbug.com/471786 +@benchmark.Disabled('reference','win') # http://crbug.com/463111 class DrawPropertiesTop25(benchmark.Benchmark): """Measures the relative performance of CalcDrawProperties vs computing draw properties from property trees.
diff --git a/tools/perf/benchmarks/dromaeo.py b/tools/perf/benchmarks/dromaeo.py index b0f7d60c..b10f0a0 100644 --- a/tools/perf/benchmarks/dromaeo.py +++ b/tools/perf/benchmarks/dromaeo.py
@@ -226,7 +226,9 @@ return 'dromaeo.jslibeventprototype' -@benchmark.Disabled('xp') # crbug.com/389731 +# xp: crbug.com/389731 +# win7: http://crbug.com/479796 +@benchmark.Disabled('xp', 'win7') class DromaeoJslibModifyJquery(_DromaeoBenchmark): """Dromaeo JSLib modify jquery JavaScript benchmark.
diff --git a/tools/perf/benchmarks/smoothness.py b/tools/perf/benchmarks/smoothness.py index b39aeeb9..eaa16f34 100644 --- a/tools/perf/benchmarks/smoothness.py +++ b/tools/perf/benchmarks/smoothness.py
@@ -205,7 +205,6 @@ class SmoothnessFlingSimpleMobilePages(benchmark.Benchmark): """Measures rendering statistics for flinging a simple mobile sites page set. """ - test = smoothness.Smoothness page_set = page_sets.SimpleMobileSitesFlingPageSet def CustomizeBrowserOptions(self, options): @@ -218,48 +217,57 @@ def Name(cls): return 'smoothness.fling.simple_mobile_sites' + def CreatePageTest(self, options): # pylint: disable=unused-argument + return smoothness.Smoothness(enable_auto_issuing_marker=False) + + @benchmark.Enabled('android', 'chromeos') class SmoothnessToughPinchZoomCases(benchmark.Benchmark): """Measures rendering statistics for pinch-zooming into the tough pinch zoom cases. """ - test = smoothness.Smoothness page_set = page_sets.ToughPinchZoomCasesPageSet @classmethod def Name(cls): return 'smoothness.tough_pinch_zoom_cases' + def CreatePageTest(self, options): # pylint: disable=unused-argument + return smoothness.Smoothness(enable_auto_issuing_marker=False) + @benchmark.Enabled('chromeos') class SmoothnessToughScrollingWhileZoomedInCases(benchmark.Benchmark): """Measures rendering statistics for pinch-zooming then diagonal scrolling""" - test = smoothness.Smoothness page_set = page_sets.ToughScrollingWhileZoomedInCasesPageSet @classmethod def Name(cls): return 'smoothness.tough_scrolling_while_zoomed_in_cases' + def CreatePageTest(self, options): # pylint: disable=unused-argument + return smoothness.Smoothness(enable_auto_issuing_marker=False) + @benchmark.Enabled('android') class SmoothnessPolymer(benchmark.Benchmark): """Measures rendering statistics for Polymer cases. """ - test = smoothness.Smoothness page_set = page_sets.PolymerPageSet @classmethod def Name(cls): return 'smoothness.polymer' + def CreatePageTest(self, options): # pylint: disable=unused-argument + return smoothness.Smoothness(enable_auto_issuing_marker=False) + @benchmark.Enabled('android') class SmoothnessGpuRasterizationPolymer(benchmark.Benchmark): """Measures rendering statistics for the Polymer cases with GPU rasterization. """ tag = 'gpu_rasterization' - test = smoothness.Smoothness page_set = page_sets.PolymerPageSet def CustomizeBrowserOptions(self, options): @@ -269,6 +277,9 @@ def Name(cls): return 'smoothness.gpu_rasterization.polymer' + def CreatePageTest(self, options): # pylint: disable=unused-argument + return smoothness.Smoothness(enable_auto_issuing_marker=False) + class SmoothnessToughFastScrollingCases(benchmark.Benchmark): test = smoothness.Smoothness @@ -316,20 +327,21 @@ class SmoothnessPathologicalMobileSites(benchmark.Benchmark): """Measures task execution statistics while scrolling pathological sites. """ - test = smoothness.Smoothness page_set = page_sets.PathologicalMobileSitesPageSet @classmethod def Name(cls): return 'smoothness.pathological_mobile_sites' + def CreatePageTest(self, options): # pylint: disable=unused-argument + return smoothness.Smoothness(enable_auto_issuing_marker=False) + @benchmark.Enabled('android') class SmoothnessSyncScrollPathologicalMobileSites(benchmark.Benchmark): """Measures task execution statistics while sync-scrolling pathological sites. """ tag = 'sync_scroll' - test = smoothness.Smoothness page_set = page_sets.PathologicalMobileSitesPageSet def CustomizeBrowserOptions(self, options): @@ -339,6 +351,10 @@ def Name(cls): return 'smoothness.sync_scroll.pathological_mobile_sites' + def CreatePageTest(self, options): # pylint: disable=unused-argument + return smoothness.Smoothness(enable_auto_issuing_marker=False) + + class SmoothnessToughAnimatedImageCases(benchmark.Benchmark): test = smoothness.Smoothness page_set = page_sets.ToughAnimatedImageCasesPageSet
diff --git a/tools/perf/measurements/measurement_smoke_test.py b/tools/perf/measurements/measurement_smoke_test.py index c8debc9..ef45aa9 100644 --- a/tools/perf/measurements/measurement_smoke_test.py +++ b/tools/perf/measurements/measurement_smoke_test.py
@@ -1,8 +1,6 @@ # 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. -"""Measurement smoke test to make sure that no new action_name_to_run is -defined.""" import logging import optparse @@ -10,6 +8,7 @@ import unittest from telemetry import benchmark as benchmark_module +from telemetry.core import browser_options from telemetry.core import discover from telemetry.page import page_test from telemetry.unittest_util import options_for_unittests @@ -42,9 +41,15 @@ for benchmark_class in all_benchmarks_classes: options = options_for_unittests.GetCopy() parser = optparse.OptionParser() - benchmark_class.AddCommandLineArgs(parser) - benchmark_module.AddCommandLineArgs(parser) - benchmark_class.SetArgumentDefaults(parser) + browser_options.BrowserOptions.AddCommandLineArgs(parser) + try: + benchmark_class.AddCommandLineArgs(parser) + benchmark_module.AddCommandLineArgs(parser) + benchmark_class.SetArgumentDefaults(parser) + except Exception: + logging.error('Exception raised when processing benchmark %s' + % benchmark_class) + raise options.MergeDefaultValues(parser.get_default_values()) pt = benchmark_class().CreatePageTest(options) if not isinstance(pt, timeline_based_measurement.TimelineBasedMeasurement):
diff --git a/tools/perf/measurements/smoothness.py b/tools/perf/measurements/smoothness.py index 12cec16..6cf2741 100644 --- a/tools/perf/measurements/smoothness.py +++ b/tools/perf/measurements/smoothness.py
@@ -8,7 +8,7 @@ class Smoothness(page_test.PageTest): - def __init__(self, enable_auto_issuing_marker=True): + def __init__(self, enable_auto_issuing_marker=False): super(Smoothness, self).__init__() self._smoothness_controller = None self._enable_auto_issuing_marker = enable_auto_issuing_marker
diff --git a/tools/perf/measurements/smoothness_unittest.py b/tools/perf/measurements/smoothness_unittest.py index a3f9506..1a4f4e9 100644 --- a/tools/perf/measurements/smoothness_unittest.py +++ b/tools/perf/measurements/smoothness_unittest.py
@@ -29,16 +29,6 @@ self.platform = FakePlatform() -class AnimatedPage(page.Page): - def __init__(self, page_set): - super(AnimatedPage, self).__init__( - url='file://animated_page.html', - page_set=page_set, base_dir=page_set.base_dir) - - def RunPageInteractions(self, action_runner): - action_runner.Wait(.2) - - class FakeTab(object): def __init__(self): self.browser = FakeBrowser() @@ -147,19 +137,5 @@ self.assertEquals(1, len(frame_lengths)) self.assertGreater(frame_lengths[0].GetRepresentativeNumber, 0) - @decorators.Disabled('mac', 'chromeos') # http://crbug.com/403903 - def testSmoothnessForPageWithNoGesture(self): - ps = self.CreateEmptyPageSet() - ps.AddUserStory(AnimatedPage(ps)) - - measurement = smoothness.Smoothness() - results = self.RunMeasurement(measurement, ps, options=self._options) - self.assertEquals(0, len(results.failures)) - - percentage_smooth = results.FindAllPageSpecificValuesNamed( - 'percentage_smooth') - self.assertEquals(len(percentage_smooth), 1) - self.assertGreaterEqual(percentage_smooth[0].GetRepresentativeNumber(), 0) - def testCleanUpTrace(self): self.TestTracingCleanedUp(smoothness.Smoothness, self._options)
diff --git a/tools/perf/measurements/thread_times_unittest.py b/tools/perf/measurements/thread_times_unittest.py index 310b98c..a6a2426 100644 --- a/tools/perf/measurements/thread_times_unittest.py +++ b/tools/perf/measurements/thread_times_unittest.py
@@ -4,14 +4,24 @@ from telemetry.core import wpr_modes from telemetry import decorators +from telemetry.page import page from telemetry.unittest_util import options_for_unittests from telemetry.unittest_util import page_test_test_case -from measurements import smoothness_unittest from measurements import thread_times from metrics import timeline +class AnimatedPage(page.Page): + def __init__(self, page_set): + super(AnimatedPage, self).__init__( + url='file://animated_page.html', + page_set=page_set, base_dir=page_set.base_dir) + + def RunPageInteractions(self, action_runner): + action_runner.Wait(.2) + + class ThreadTimesUnitTest(page_test_test_case.PageTestTestCase): def setUp(self): self._options = options_for_unittests.GetCopy() @@ -32,7 +42,7 @@ def testBasicForPageWithNoGesture(self): ps = self.CreateEmptyPageSet() - ps.AddUserStory(smoothness_unittest.AnimatedPage(ps)) + ps.AddUserStory(AnimatedPage(ps)) measurement = thread_times.ThreadTimes() timeline_options = self._options
diff --git a/tools/perf/page_sets/image_decoding_cases.py b/tools/perf/page_sets/image_decoding_cases.py index a213115..335040f6 100644 --- a/tools/perf/page_sets/image_decoding_cases.py +++ b/tools/perf/page_sets/image_decoding_cases.py
@@ -10,7 +10,8 @@ super(ImageDecodingCasesPage, self).__init__(url=url, page_set=page_set) def RunPageInteractions(self, action_runner): - action_runner.Wait(5) + with action_runner.CreateInteraction('DecodeImage'): + action_runner.Wait(5) class ImageDecodingCasesPageSet(page_set_module.PageSet):
diff --git a/tools/perf/page_sets/maps.py b/tools/perf/page_sets/maps.py index ed49983f..5bf64cd 100644 --- a/tools/perf/page_sets/maps.py +++ b/tools/perf/page_sets/maps.py
@@ -24,7 +24,7 @@ action_runner.Wait(3) def RunPageInteractions(self, action_runner): - with action_runner.RunPageInteractions('MapAnimation'): + with action_runner.CreateInteraction('MapAnimation'): action_runner.WaitForJavaScriptCondition('window.testDone', 120)
diff --git a/tools/perf/page_sets/polymer.py b/tools/perf/page_sets/polymer.py index fa90a7a..bbab4d4 100644 --- a/tools/perf/page_sets/polymer.py +++ b/tools/perf/page_sets/polymer.py
@@ -97,13 +97,14 @@ page_set=page_set, run_no_page_interactions=run_no_page_interactions) def PerformPageInteractions(self, action_runner): - action_runner.ExecuteJavaScript( - "document.getElementById('fab').scrollIntoView()") - action_runner.Wait(5) - self.AnimateShadow(action_runner, 'card') - #FIXME(wiltzius) disabling until this issue is fixed: - # https://github.com/Polymer/paper-shadow/issues/12 - #self.AnimateShadow(action_runner, 'fab') + with action_runner.CreateInteraction('ScrollAndShadowAnimation'): + action_runner.ExecuteJavaScript( + "document.getElementById('fab').scrollIntoView()") + action_runner.Wait(5) + self.AnimateShadow(action_runner, 'card') + #FIXME(wiltzius) disabling until this issue is fixed: + # https://github.com/Polymer/paper-shadow/issues/12 + #self.AnimateShadow(action_runner, 'fab') def AnimateShadow(self, action_runner, eid): for i in range(1, 6):
diff --git a/tools/perf/page_sets/tough_animation_cases.py b/tools/perf/page_sets/tough_animation_cases.py index 5c458d8..485926a 100644 --- a/tools/perf/page_sets/tough_animation_cases.py +++ b/tools/perf/page_sets/tough_animation_cases.py
@@ -18,7 +18,8 @@ action_runner.WaitForJavaScriptCondition('window.measurementReady') def RunPageInteractions(self, action_runner): - action_runner.Wait(10) + with action_runner.CreateInteraction('ToughAnimation'): + action_runner.Wait(10) class ToughAnimationCasesPageSet(page_set_module.PageSet):
diff --git a/tools/perf/page_sets/tough_canvas_cases.py b/tools/perf/page_sets/tough_canvas_cases.py index 292e911..dd8f9f5 100644 --- a/tools/perf/page_sets/tough_canvas_cases.py +++ b/tools/perf/page_sets/tough_canvas_cases.py
@@ -17,7 +17,8 @@ "document.readyState == 'complete'") def RunPageInteractions(self, action_runner): - action_runner.Wait(5) + with action_runner.CreateInteraction('CanvasAnimation'): + action_runner.Wait(5) class MicrosofFirefliesPage(ToughCanvasCasesPage):
diff --git a/tools/perf/page_sets/tough_filters_cases.py b/tools/perf/page_sets/tough_filters_cases.py index 6b6fc04..a51e961e 100644 --- a/tools/perf/page_sets/tough_filters_cases.py +++ b/tools/perf/page_sets/tough_filters_cases.py
@@ -8,15 +8,17 @@ class ToughFiltersCasesPage(page_module.Page): def RunPageInteractions(self, action_runner): - action_runner.Wait(10) + with action_runner.CreateInteraction('Filter'): + action_runner.Wait(10) class PirateMarkPage(page_module.Page): def RunPageInteractions(self, action_runner): - action_runner.EvaluateJavaScript( - 'document.getElementById("benchmarkButtonText").click()') - action_runner.Wait(10) + with action_runner.CreateInteraction('Filter'): + action_runner.EvaluateJavaScript( + 'document.getElementById("benchmarkButtonText").click()') + action_runner.Wait(10) class ToughFiltersCasesPageSet(page_set_module.PageSet):
diff --git a/tools/perf/page_sets/tough_scheduling_cases.py b/tools/perf/page_sets/tough_scheduling_cases.py index 275f470..6ea808e 100644 --- a/tools/perf/page_sets/tough_scheduling_cases.py +++ b/tools/perf/page_sets/tough_scheduling_cases.py
@@ -293,7 +293,8 @@ page_set=page_set) def RunPageInteractions(self, action_runner): - action_runner.Wait(3) + with action_runner.CreateInteraction('SplitAnimation'): + action_runner.Wait(3) class Page20(ToughSchedulingCasesPage):
diff --git a/tools/perf/page_sets/tough_webgl_cases.py b/tools/perf/page_sets/tough_webgl_cases.py index 461eb55e..099fb9a 100644 --- a/tools/perf/page_sets/tough_webgl_cases.py +++ b/tools/perf/page_sets/tough_webgl_cases.py
@@ -27,7 +27,8 @@ action_runner.Wait(2) def RunPageInteractions(self, action_runner): - action_runner.Wait(5) + with action_runner.CreateInteraction('WebGLAnimation'): + action_runner.Wait(5) class ToughWebglCasesPageSet(page_set_module.PageSet):
diff --git a/tools/perf/profile_creators/cookie_profile_extender.py b/tools/perf/profile_creators/cookie_profile_extender.py index b98af38..ad9e85d8 100644 --- a/tools/perf/profile_creators/cookie_profile_extender.py +++ b/tools/perf/profile_creators/cookie_profile_extender.py
@@ -20,12 +20,13 @@ """ _COOKIE_DB_EXPECTED_SIZE = 3300 - def __init__(self): + def __init__(self, finder_options): # The rate limiting factors are fetching network resources and executing # javascript. There's not much to be done about the former, and having one # tab per logical core appears close to optimum for the latter. maximum_batch_size = multiprocessing.cpu_count() - super(CookieProfileExtender, self).__init__(maximum_batch_size) + super(CookieProfileExtender, self).__init__( + finder_options, maximum_batch_size) # A list of urls that have not yet been navigated to. This list will shrink # over time. Each navigation will add a diminishing number of new cookies,
diff --git a/tools/perf/profile_creators/fast_navigation_profile_extender.py b/tools/perf/profile_creators/fast_navigation_profile_extender.py index 7fa222a..d5b3da7 100644 --- a/tools/perf/profile_creators/fast_navigation_profile_extender.py +++ b/tools/perf/profile_creators/fast_navigation_profile_extender.py
@@ -4,8 +4,6 @@ import time from profile_creators import profile_extender -from telemetry.core import browser_finder -from telemetry.core import browser_finder_exceptions from telemetry.core import exceptions @@ -21,14 +19,14 @@ with the number of batches, but does not scale with the size of the batch. """ - def __init__(self, maximum_batch_size): + def __init__(self, finder_options, maximum_batch_size): """Initializer. Args: maximum_batch_size: A positive integer indicating the number of tabs to simultaneously perform navigations. """ - super(FastNavigationProfileExtender, self).__init__() + super(FastNavigationProfileExtender, self).__init__(finder_options) # The instance keeps a list of Tabs that can be navigated successfully. # This means that the Tab is not crashed, and is processing JavaScript in a @@ -44,19 +42,13 @@ # The default amount of time to wait for the retrieval of the URL of a tab. self._TAB_URL_RETRIEVAL_TIMEOUT_IN_SECONDS = 1 - def Run(self, finder_options): - """Extends the profile. - - Args: - finder_options: An instance of BrowserFinderOptions that contains the - directory of the input profile, the directory to place the output - profile, and sufficient information to choose a specific browser binary. - """ + def Run(self): + """Superclass override.""" try: - self.SetUp(finder_options) + self.SetUpBrowser() self._PerformNavigations() finally: - self.TearDown() + self.TearDownBrowser() def GetUrlIterator(self): """Gets URLs for the browser to navigate to. @@ -83,10 +75,6 @@ """ pass - @property - def profile_path(self): - return self._profile_path - def _AddNewTab(self): """Adds a new tab to the browser.""" @@ -124,18 +112,6 @@ since there is no guarantee that the tab can be safely removed.""" self._navigation_tabs.remove(tab) - def _GetPossibleBrowser(self, finder_options): - """Return a possible_browser with the given options.""" - possible_browser = browser_finder.FindBrowser(finder_options) - if not possible_browser: - raise browser_finder_exceptions.BrowserFinderException( - 'No browser found.\n\nAvailable browsers:\n%s\n' % - '\n'.join(browser_finder.GetAllAvailableBrowserTypes(finder_options))) - finder_options.browser_options.browser_type = ( - possible_browser.browser_type) - - return possible_browser - def _RetrieveTabUrl(self, tab, timeout): """Retrives the URL of the tab.""" try:
diff --git a/tools/perf/profile_creators/fast_navigation_profile_extender_unittest.py b/tools/perf/profile_creators/fast_navigation_profile_extender_unittest.py index 05cf334e..c514947 100644 --- a/tools/perf/profile_creators/fast_navigation_profile_extender_unittest.py +++ b/tools/perf/profile_creators/fast_navigation_profile_extender_unittest.py
@@ -6,6 +6,7 @@ from profile_creators.fast_navigation_profile_extender import ( FastNavigationProfileExtender) from telemetry.core import util +from telemetry.unittest_util import options_for_unittests util.AddDirToPythonPath(util.GetTelemetryDir(), 'third_party', 'mock') import mock @@ -38,7 +39,8 @@ class FastNavigationProfileExtenderTest(unittest.TestCase): def testPerformNavigations(self): maximum_batch_size = 15 - extender = FastNavigationProfileExtender(maximum_batch_size) + options = options_for_unittests.GetCopy() + extender = FastNavigationProfileExtender(options, maximum_batch_size) navigation_urls = [] for i in range(extender._NUM_TABS):
diff --git a/tools/perf/profile_creators/history_profile_extender.py b/tools/perf/profile_creators/history_profile_extender.py index 58293c3..f68548f3 100644 --- a/tools/perf/profile_creators/history_profile_extender.py +++ b/tools/perf/profile_creators/history_profile_extender.py
@@ -14,13 +14,14 @@ files. It continues running until the history DB becomes full.""" _HISTORY_DB_MAX_SIZE_IN_MB = 10 - def __init__(self): + def __init__(self, finder_options): # The rate limiting factors are the speed of page navigation, and the speed # of python bindings. The former is larger than the latter, so having a # large batch size skews the amortized average time per page load towards # the latter. maximum_batch_size = multiprocessing.cpu_count() * 2 - super(HistoryProfileExtender, self).__init__(maximum_batch_size) + super(HistoryProfileExtender, self).__init__( + finder_options, maximum_batch_size) # A list of paths of temporary files. The instance is responsible for # making sure that the files are deleted before they are removed from this @@ -64,9 +65,9 @@ """Superclass override.""" return self._IsHistoryDBAtMaxSize() - def TearDown(self): + def TearDownBrowser(self): """Superclass override.""" - super(HistoryProfileExtender, self).TearDown() + super(HistoryProfileExtender, self).TearDownBrowser() for path in self._generated_temp_files: os.remove(path) self._generated_temp_files = []
diff --git a/tools/perf/profile_creators/history_profile_extender_unittest.py b/tools/perf/profile_creators/history_profile_extender_unittest.py index c0b0b0f0..46997cd 100644 --- a/tools/perf/profile_creators/history_profile_extender_unittest.py +++ b/tools/perf/profile_creators/history_profile_extender_unittest.py
@@ -21,7 +21,9 @@ # The profile extender does not work on Android or ChromeOS. @decorators.Disabled('android', 'chromeos') def testFullFunctionality(self): - extender = HistoryProfileExtender() + options = options_for_unittests.GetCopy() + options.output_profile_path = tempfile.mkdtemp() + extender = HistoryProfileExtender(options) # Stop the extender at the earliest possible opportunity. extender.ShouldExitAfterBatchNavigation = mock.MagicMock(return_value=True) @@ -29,11 +31,8 @@ # static, small number to increase the speed of the test. extender._NUM_TABS = 3 - options = options_for_unittests.GetCopy() - options.output_profile_path = tempfile.mkdtemp() - try: - extender.Run(options) + extender.Run() self.assertEquals(extender.profile_path, options.output_profile_path) self.assertTrue(os.path.exists(extender.profile_path)) history_db_path = os.path.join(extender.profile_path, "Default",
diff --git a/tools/perf/profile_creators/large_profile_extender.py b/tools/perf/profile_creators/large_profile_extender.py index 8b61efc..56bd4ff 100644 --- a/tools/perf/profile_creators/large_profile_extender.py +++ b/tools/perf/profile_creators/large_profile_extender.py
@@ -9,9 +9,11 @@ class LargeProfileExtender(profile_extender.ProfileExtender): """This class creates a large profile by performing a large number of url navigations.""" - def Run(self, options): - extender = history_profile_extender.HistoryProfileExtender() - extender.Run(options) + def Run(self): + extender = history_profile_extender.HistoryProfileExtender( + self.finder_options) + extender.Run() - extender = cookie_profile_extender.CookieProfileExtender() - extender.Run(options) + extender = cookie_profile_extender.CookieProfileExtender( + self.finder_options) + extender.Run()
diff --git a/tools/perf/profile_creators/profile_extender.py b/tools/perf/profile_creators/profile_extender.py index f69957a..b1dccfa6 100644 --- a/tools/perf/profile_creators/profile_extender.py +++ b/tools/perf/profile_creators/profile_extender.py
@@ -2,6 +2,8 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +from telemetry.core import browser_finder +from telemetry.core import browser_finder_exceptions from telemetry.core import platform from telemetry.core import wpr_modes @@ -9,31 +11,30 @@ class ProfileExtender(object): """Abstract base class for an object that constructs a Chrome profile.""" - def __init__(self): - # The path of the profile that the browser will use while it's running. - # This member is initialized during SetUp(). - self._profile_path = None + def __init__(self, finder_options): + """Initializer. + + |finder_options| is an instance of BrowserFinderOptions. When subclass + implementations of this method inevitably attempt to find and launch a + browser, they should pass |finder_options| to the relevant methods. + + Several properties of |finder_options| might require direct manipulation by + subclasses. These are: + |finder_options.output_profile_path|: The path at which the profile + should be created. + |finder_options.browser_options.profile_dir|: If this property is None, + then a new profile is created. Otherwise, the existing profile is + appended on to. + """ + self._finder_options = finder_options # A reference to the browser that will be performing all of the tab # navigations. - # This member is initialized during SetUp(). + # This member is initialized during SetUpBrowser(). self._browser = None - def Run(self, options): - """Creates or extends the profile. - - |options| is an instance of BrowserFinderOptions. When subclass - implementations of this method inevitably attempt to find and launch a - browser, they should pass |options| to the relevant methods. - - Several properties of |options| might require direct manipulation by - subclasses. These are: - |options.output_profile_path|: The path at which the profile should be - created. - |options.browser_options.profile_dir|: If this property is None, then a - new profile is created. Otherwise, the existing profile is appended on - to. - """ + def Run(self): + """Creates or extends the profile.""" raise NotImplementedError() def WebPageReplayArchivePath(self): @@ -44,35 +45,43 @@ return None @property + def finder_options(self): + """The options to use to find and run the browser.""" + return self._finder_options + + @property def profile_path(self): - return self._profile_path + """The path of the profile that the browser will use while it's running.""" + return self.finder_options.output_profile_path @property def browser(self): return self._browser - def SetUp(self, finder_options): + def SetUpBrowser(self): """Finds and starts the browser. - Can be overridden by subclasses. Subclasses must call the super class - implementation. + Can be overridden by subclasses. The subclass implementation must call the + super class implementation. + + Subclasses do not need to call this method. This method is only necessary + if the subclass needs to start a browser. If a subclass does call this + method, the subclass must also call TearDownBrowser(). """ - self._profile_path = finder_options.output_profile_path - possible_browser = self._GetPossibleBrowser(finder_options) + possible_browser = self._GetPossibleBrowser(self.finder_options) assert possible_browser.supports_tab_control assert (platform.GetHostPlatform().GetOSName() in ["win", "mac", "linux"]) - self._SetUpWebPageReplay(finder_options, possible_browser) - self._browser = possible_browser.Create(finder_options) + self._SetUpWebPageReplay(self.finder_options, possible_browser) + self._browser = possible_browser.Create(self.finder_options) - def TearDown(self): - """Teardown that is guaranteed to be executed before the instance is - destroyed. + def TearDownBrowser(self): + """Tears down the browser. - Can be overridden by subclasses. Subclasses must call the super class - implementation. + Can be overridden by subclasses. The subclass implementation must call the + super class implementation. """ if self._browser: self._browser.Close() @@ -108,3 +117,16 @@ network_controller.SetReplayArgs( wpr_archive_path, browser_options.wpr_mode, browser_options.netsim, browser_options.extra_wpr_args, make_javascript_deterministic) + + def _GetPossibleBrowser(self, finder_options): + """Return a possible_browser with the given options.""" + possible_browser = browser_finder.FindBrowser(finder_options) + if not possible_browser: + raise browser_finder_exceptions.BrowserFinderException( + 'No browser found.\n\nAvailable browsers:\n%s\n' % + '\n'.join(browser_finder.GetAllAvailableBrowserTypes(finder_options))) + finder_options.browser_options.browser_type = ( + possible_browser.browser_type) + + return possible_browser +
diff --git a/tools/perf/profile_creators/profile_generator.py b/tools/perf/profile_creators/profile_generator.py index 784f8aadf..cbbc85e 100644 --- a/tools/perf/profile_creators/profile_generator.py +++ b/tools/perf/profile_creators/profile_generator.py
@@ -70,9 +70,9 @@ temp_output_directory = tempfile.mkdtemp() options.output_profile_path = temp_output_directory - profile_creator_instance = profile_extender_class() + profile_creator_instance = profile_extender_class(options) try: - profile_creator_instance.Run(options) + profile_creator_instance.Run() except Exception as e: logging.exception('Profile creation failed.') shutil.rmtree(temp_output_directory)
diff --git a/tools/perf/profile_creators/small_profile_extender.py b/tools/perf/profile_creators/small_profile_extender.py index 2fc64c9..ff9a793 100644 --- a/tools/perf/profile_creators/small_profile_extender.py +++ b/tools/perf/profile_creators/small_profile_extender.py
@@ -11,11 +11,12 @@ fast_navigation_profile_extender.FastNavigationProfileExtender): """Creates a small profile by performing 25 navigations.""" - def __init__(self): + def __init__(self, finder_options): # Use exactly 5 tabs to generate the profile. This is because consumers of # this profile will perform a session restore, and expect 5 restored tabs. maximum_batch_size = 5 - super(SmallProfileExtender, self).__init__(maximum_batch_size) + super(SmallProfileExtender, self).__init__( + finder_options, maximum_batch_size) # Get the list of urls from the typical 25 page set. self._page_set = page_sets.Typical25PageSet()
diff --git a/tools/perf/pylintrc b/tools/perf/pylintrc new file mode 100644 index 0000000..b112c527 --- /dev/null +++ b/tools/perf/pylintrc
@@ -0,0 +1,17 @@ +[MESSAGES CONTROL] + +# Disable the message, report, category or checker with the given id(s). +# TODO(nednguyen): Remove unusued-argument and unused-import from this list. +disable=I0010,I0011,abstract-class-little-used,abstract-class-not-used,anomalous-backslash-in-string,bad-builtin,bad-context-manager,bad-continuation,bad-indentation,bad-str-strip-call,bad-whitespace,broad-except,cell-var-from-loop,deprecated-lambda,deprecated-module,duplicate-code,eval-used,exec-used,fixme,function-redefined,global-statement,import-error,interface-not-implemented,invalid-name,locally-enabled,logging-not-lazy,missing-docstring,missing-final-newline,no-init,no-member,no-name-in-module,no-self-use,no-self-use,not-callable,old-style-class,protected-access,reimported,star-args,super-on-old-class,superfluous-parens,too-few-public-methods,too-many-ancestors,too-many-arguments,too-many-branches,too-many-function-args,too-many-instance-attributes,too-many-lines,too-many-locals,too-many-public-methods,too-many-return-statements,too-many-statements,trailing-whitespace,unnecessary-semicolon,unpacking-non-sequence,useless-else-on-loop,unused-argument,unused-import + + +[REPORTS] + +# Don't write out full reports, just messages. +reports=no + + +[FORMAT] + +# We use two spaces for indents, instead of the usual four spaces or tab. +indent-string=' '
diff --git a/tools/perf_expectations/perf_expectations.json b/tools/perf_expectations/perf_expectations.json index 252be4f7..858241e5 100644 --- a/tools/perf_expectations/perf_expectations.json +++ b/tools/perf_expectations/perf_expectations.json
@@ -374,7 +374,7 @@ "linux-release/sizes/nacl_helper-si/initializers": {"reva": 271321, "revb": 271321, "type": "absolute", "better": "lower", "improve": 6, "regress": 8, "sha1": "3394be7f"}, "linux-release/sizes/nacl_helper-text/text": {"reva": 320500, "revb": 320622, "type": "absolute", "better": "lower", "improve": 5754494, "regress": 6373632, "sha1": "a61cbb0f"}, "linux-release/sizes/nacl_helper-textrel/textrel": {"reva": 264283, "revb": 264283, "type": "absolute", "better": "lower", "improve": 0, "regress": 0, "sha1": "0efaa99f"}, - "linux-release/sizes/nacl_helper/nacl_helper": {"reva": 316558, "revb": 316807, "type": "absolute", "better": "lower", "improve": 7712027, "regress": 8529465, "sha1": "72d6bac9"}, + "linux-release/sizes/nacl_helper/nacl_helper": {"reva": 326388, "revb": 326429, "type": "absolute", "better": "lower", "improve": 8103256, "regress": 8960541, "sha1": "58f1a213"}, "linux-release/sizes/nacl_helper_bootstrap-bss/bss": {"reva": 114822, "revb": 115019, "type": "absolute", "better": "lower", "improve": 1019980807, "regress": 1127347209, "sha1": "a4ff54ab"}, "linux-release/sizes/nacl_helper_bootstrap-data/data": {"reva": 114822, "revb": 115019, "type": "absolute", "better": "lower", "improve": 19, "regress": 21, "sha1": "6a3e92f4"}, "linux-release/sizes/nacl_helper_bootstrap-si/initializers": {"reva": 114822, "revb": 115019, "type": "absolute", "better": "lower", "improve": 0, "regress": 0, "sha1": "dd908f29"},
diff --git a/tools/telemetry/PRESUBMIT.py b/tools/telemetry/PRESUBMIT.py index b376189..89909f1 100644 --- a/tools/telemetry/PRESUBMIT.py +++ b/tools/telemetry/PRESUBMIT.py
@@ -4,8 +4,6 @@ import os import sys -PYLINT_BLACKLIST = [] -PYLINT_DISABLED_WARNINGS = ['R0923', 'R0201', 'E1101'] def _CommonChecks(input_api, output_api): results = [] @@ -21,9 +19,7 @@ '$ %s' % os.path.abspath(update_docs_path))) results.extend(input_api.canned_checks.RunPylint( - input_api, output_api, - black_list=PYLINT_BLACKLIST, - disabled_warnings=PYLINT_DISABLED_WARNINGS)) + input_api, output_api, black_list=[], pylintrc='pylintrc')) return results def GetPathsToPrepend(input_api):
diff --git a/tools/telemetry/pylintrc b/tools/telemetry/pylintrc new file mode 100644 index 0000000..b112c527 --- /dev/null +++ b/tools/telemetry/pylintrc
@@ -0,0 +1,17 @@ +[MESSAGES CONTROL] + +# Disable the message, report, category or checker with the given id(s). +# TODO(nednguyen): Remove unusued-argument and unused-import from this list. +disable=I0010,I0011,abstract-class-little-used,abstract-class-not-used,anomalous-backslash-in-string,bad-builtin,bad-context-manager,bad-continuation,bad-indentation,bad-str-strip-call,bad-whitespace,broad-except,cell-var-from-loop,deprecated-lambda,deprecated-module,duplicate-code,eval-used,exec-used,fixme,function-redefined,global-statement,import-error,interface-not-implemented,invalid-name,locally-enabled,logging-not-lazy,missing-docstring,missing-final-newline,no-init,no-member,no-name-in-module,no-self-use,no-self-use,not-callable,old-style-class,protected-access,reimported,star-args,super-on-old-class,superfluous-parens,too-few-public-methods,too-many-ancestors,too-many-arguments,too-many-branches,too-many-function-args,too-many-instance-attributes,too-many-lines,too-many-locals,too-many-public-methods,too-many-return-statements,too-many-statements,trailing-whitespace,unnecessary-semicolon,unpacking-non-sequence,useless-else-on-loop,unused-argument,unused-import + + +[REPORTS] + +# Don't write out full reports, just messages. +reports=no + + +[FORMAT] + +# We use two spaces for indents, instead of the usual four spaces or tab. +indent-string=' '
diff --git a/tools/telemetry/telemetry/core/discover.py b/tools/telemetry/telemetry/core/discover.py index f612bfe..84fe5823 100644 --- a/tools/telemetry/telemetry/core/discover.py +++ b/tools/telemetry/telemetry/core/discover.py
@@ -52,7 +52,7 @@ # and class names, then always index by class name. @decorators.Cache def DiscoverClasses(start_dir, top_level_dir, base_class, pattern='*', - index_by_class_name=False): + index_by_class_name=True): """Discover all classes in |start_dir| which subclass |base_class|. Base classes that contain subclasses are ignored by default.
diff --git a/tools/telemetry/telemetry/core/discover_unittest.py b/tools/telemetry/telemetry/core/discover_unittest.py index ba2bf70..1bdcf87 100644 --- a/tools/telemetry/telemetry/core/discover_unittest.py +++ b/tools/telemetry/telemetry/core/discover_unittest.py
@@ -14,19 +14,45 @@ self._start_dir = os.path.join(self._base_dir, 'discoverable_classes') self._base_class = Exception - def testDiscoverClassesBasic(self): + def testDiscoverClassesWithIndexByModuleName(self): + classes = discover.DiscoverClasses( + self._start_dir, self._base_dir, self._base_class, + index_by_class_name=False) + + actual_classes = dict( + (name, cls.__name__) for name, cls in classes.iteritems()) + expected_classes = { + 'another_discover_dummyclass': 'DummyExceptionImpl2', + 'discover_dummyclass': 'DummyException', + } + self.assertEqual(actual_classes, expected_classes) + + def testDiscoverClassesWithIndexByClassName(self): classes = discover.DiscoverClasses( self._start_dir, self._base_dir, self._base_class) actual_classes = dict( (name, cls.__name__) for name, cls in classes.iteritems()) expected_classes = { - 'discover_dummyclass': 'DummyException', + 'dummy_exception': 'DummyException', + 'dummy_exception_impl1': 'DummyExceptionImpl1', + 'dummy_exception_impl2': 'DummyExceptionImpl2' + } + self.assertEqual(actual_classes, expected_classes) + + def testDiscoverClassesWithPatternAndIndexByModule(self): + classes = discover.DiscoverClasses( + self._start_dir, self._base_dir, self._base_class, + pattern='another*', index_by_class_name=False) + + actual_classes = dict( + (name, cls.__name__) for name, cls in classes.iteritems()) + expected_classes = { 'another_discover_dummyclass': 'DummyExceptionImpl2', } self.assertEqual(actual_classes, expected_classes) - def testDiscoverClassesWithPattern(self): + def testDiscoverClassesWithPatternAndIndexByClassName(self): classes = discover.DiscoverClasses( self._start_dir, self._base_dir, self._base_class, pattern='another*') @@ -34,20 +60,7 @@ actual_classes = dict( (name, cls.__name__) for name, cls in classes.iteritems()) expected_classes = { - 'another_discover_dummyclass': 'DummyExceptionImpl2', - } - self.assertEqual(actual_classes, expected_classes) - - def testDiscoverClassesByClassName(self): - classes = discover.DiscoverClasses( - self._start_dir, self._base_dir, self._base_class, - index_by_class_name=True) - - actual_classes = dict( - (name, cls.__name__) for name, cls in classes.iteritems()) - expected_classes = { - 'dummy_exception': 'DummyException', 'dummy_exception_impl1': 'DummyExceptionImpl1', - 'dummy_exception_impl2': 'DummyExceptionImpl2', + 'dummy_exception_impl2': 'DummyExceptionImpl2' } self.assertEqual(actual_classes, expected_classes)
diff --git a/tools/telemetry/telemetry/core/platform/android_platform_backend.py b/tools/telemetry/telemetry/core/platform/android_platform_backend.py index f3539e7..00aad43 100644 --- a/tools/telemetry/telemetry/core/platform/android_platform_backend.py +++ b/tools/telemetry/telemetry/core/platform/android_platform_backend.py
@@ -38,6 +38,7 @@ # Get build/android scripts into our path. util.AddDirToPythonPath(util.GetChromiumSrcDir(), 'build', 'android') +from pylib.device import battery_utils # pylint: disable=F0401 from pylib.device import device_errors # pylint: disable=F0401 from pylib.perf import cache_control # pylint: disable=F0401 from pylib.perf import perf_control # pylint: disable=F0401 @@ -70,6 +71,7 @@ # Ignore result. self._adb.EnableAdbRoot() self._device = self._adb.device() + self._battery = battery_utils.BatteryUtils(self._device) self._enable_performance_mode = device.enable_performance_mode self._surface_stats_collector = None self._perf_tests_setup = perf_control.PerfControl(self._device) @@ -80,7 +82,7 @@ power_controller = power_monitor_controller.PowerMonitorController([ monsoon_power_monitor.MonsoonPowerMonitor(self._device, self), android_ds2784_power_monitor.DS2784PowerMonitor(self._device, self), - android_dumpsys_power_monitor.DumpsysPowerMonitor(self._device, self), + android_dumpsys_power_monitor.DumpsysPowerMonitor(self._battery, self), ]) self._power_monitor = android_temperature_monitor.AndroidTemperatureMonitor( power_controller, self._device)
diff --git a/tools/telemetry/telemetry/core/platform/linux_platform_backend_unittest.py b/tools/telemetry/telemetry/core/platform/linux_platform_backend_unittest.py index 8727779..1059285d 100644 --- a/tools/telemetry/telemetry/core/platform/linux_platform_backend_unittest.py +++ b/tools/telemetry/telemetry/core/platform/linux_platform_backend_unittest.py
@@ -9,55 +9,35 @@ from telemetry.core import util from telemetry import decorators -class TestBackend( - linux_platform_backend.LinuxPlatformBackend): - - def __init__(self): - super(TestBackend, self).__init__() - self._mock_files = {} - - def SetMockFile(self, filename, output): - self._mock_files[filename] = output - - def GetFileContents(self, filename): - return self._mock_files[filename] - - def IsThermallyThrottled(self): - raise NotImplementedError() - - def HasBeenThermallyThrottled(self): - raise NotImplementedError() - - def GetSystemCommitCharge(self): - raise NotImplementedError() - - def StopVideoCapture(self): - raise NotImplementedError() - - def StartVideoCapture(self, min_bitrate_mbps): - raise NotImplementedError() - - def GetSystemTotalPhysicalMemory(self): - raise NotImplementedError() +util.AddDirToPythonPath(util.GetTelemetryDir(), 'third_party', 'mock') +import mock class LinuxPlatformBackendTest(unittest.TestCase): @decorators.Enabled('linux') def testGetOSVersionNameSaucy(self): - backend = TestBackend() path = os.path.join(util.GetUnittestDataDir(), 'ubuntu-saucy-lsb-release') with open(path) as f: - backend.SetMockFile('/etc/lsb-release', f.read()) + unbuntu_saucy_lsb_release_content = f.read() - self.assertEqual(backend.GetOSVersionName(), 'saucy') + with mock.patch.object( + linux_platform_backend.LinuxPlatformBackend, 'GetFileContents', + return_value=unbuntu_saucy_lsb_release_content) as mock_method: + backend = linux_platform_backend.LinuxPlatformBackend() + self.assertEqual(backend.GetOSVersionName(), 'saucy') + mock_method.assert_called_once_with('/etc/lsb-release') @decorators.Enabled('linux') def testGetOSVersionNameArch(self): - backend = TestBackend() path = os.path.join(util.GetUnittestDataDir(), 'arch-lsb-release') with open(path) as f: - backend.SetMockFile('/etc/lsb-release', f.read()) + arch_lsb_release_content = f.read() - # a distribution may not have a codename or a release number. We just check - # that GetOSVersionName doesn't raise an exception - backend.GetOSVersionName() + with mock.patch.object( + linux_platform_backend.LinuxPlatformBackend, 'GetFileContents', + return_value=arch_lsb_release_content) as mock_method: + backend = linux_platform_backend.LinuxPlatformBackend() + # a distribution may not have a codename or a release number. We just + # check that GetOSVersionName doesn't raise an exception + backend.GetOSVersionName() + mock_method.assert_called_once_with('/etc/lsb-release')
diff --git a/tools/telemetry/telemetry/core/platform/platform_backend_unittest.py b/tools/telemetry/telemetry/core/platform/platform_backend_unittest.py index 751e9f19..b4efa57 100644 --- a/tools/telemetry/telemetry/core/platform/platform_backend_unittest.py +++ b/tools/telemetry/telemetry/core/platform/platform_backend_unittest.py
@@ -11,7 +11,8 @@ class PlatformBackendTest(unittest.TestCase): - @decorators.Disabled('mac') # http://crbug.com/440666 + @decorators.Disabled('mac', # http://crbug.com/440666 + 'vista') # http://crbug.com/479337 def testPowerMonitoringSync(self): # Tests that the act of monitoring power doesn't blow up. platform = platform_module.GetHostPlatform()
diff --git a/tools/telemetry/telemetry/core/platform/power_monitor/android_dumpsys_power_monitor.py b/tools/telemetry/telemetry/core/platform/power_monitor/android_dumpsys_power_monitor.py index e871441493..f6da6ace 100644 --- a/tools/telemetry/telemetry/core/platform/power_monitor/android_dumpsys_power_monitor.py +++ b/tools/telemetry/telemetry/core/platform/power_monitor/android_dumpsys_power_monitor.py
@@ -9,9 +9,6 @@ from telemetry.core import util from telemetry.core.platform.power_monitor import sysfs_power_monitor -# Get build/android scripts into path -util.AddDirToPythonPath(util.GetChromiumSrcDir(), 'build', 'android') -from pylib.device import battery_utils # pylint: disable=F0401 class DumpsysPowerMonitor(sysfs_power_monitor.SysfsPowerMonitor): """PowerMonitor that relies on the dumpsys batterystats to monitor the power @@ -19,16 +16,16 @@ and is the same information end-users see with the battery application. Available on Android L and higher releases. """ - def __init__(self, device, platform_backend): + def __init__(self, battery, platform_backend): """Constructor. Args: - device: A DeviceUtil instance. + battery: A BatteryUtil instance. platform_backend: A LinuxBasedPlatformBackend instance. """ super(DumpsysPowerMonitor, self).__init__(platform_backend) + self._battery = battery self._browser = None - self._battery = battery_utils.BatteryUtils(device) def CanMonitorPower(self): result = self._platform.RunCommand('dumpsys batterystats -c')
diff --git a/tools/telemetry/telemetry/core/platform/power_monitor/msr_power_monitor_unittest.py b/tools/telemetry/telemetry/core/platform/power_monitor/msr_power_monitor_unittest.py index 68f925e2..1db6624 100644 --- a/tools/telemetry/telemetry/core/platform/power_monitor/msr_power_monitor_unittest.py +++ b/tools/telemetry/telemetry/core/platform/power_monitor/msr_power_monitor_unittest.py
@@ -12,7 +12,7 @@ class MsrPowerMonitorTest(unittest.TestCase): - @decorators.Enabled('win') + @decorators.Enabled('xp', 'win7', 'win8') # http://crbug.com/479337 def testMsrRuns(self): platform_backend = win_platform_backend.WinPlatformBackend() power_monitor = msr_power_monitor.MsrPowerMonitor(platform_backend)
diff --git a/tools/telemetry/telemetry/core/platform/win_platform_backend.py b/tools/telemetry/telemetry/core/platform/win_platform_backend.py index 5be2428..938414e 100644 --- a/tools/telemetry/telemetry/core/platform/win_platform_backend.py +++ b/tools/telemetry/telemetry/core/platform/win_platform_backend.py
@@ -60,7 +60,8 @@ # Check for WinRing0 and download if needed. if not (os.path.exists(dll_path) and os.path.exists(driver_path)): - win_binary_dir = os.path.join(path.GetTelemetryDir(), 'bin', 'win') + win_binary_dir = os.path.join( + path.GetTelemetryDir(), 'bin', 'win', 'AMD64') zip_path = os.path.join(win_binary_dir, 'winring0.zip') cloud_storage.GetIfChanged(zip_path, bucket=cloud_storage.PUBLIC_BUCKET) try:
diff --git a/tools/telemetry/telemetry/timeline/event_container.py b/tools/telemetry/telemetry/timeline/event_container.py index d145b03..6c1b3c2 100644 --- a/tools/telemetry/telemetry/timeline/event_container.py +++ b/tools/telemetry/telemetry/timeline/event_container.py
@@ -15,6 +15,10 @@ self.parent = parent self.name = name + @staticmethod + def IsAsyncSlice(t): + return t == async_slice_module.AsyncSlice + # Basic functions that subclasses of TimelineEventContainer should implement # in order to expose their events. New methods should be added to this part of # the code only when absolutely certain they're needed. @@ -113,13 +117,17 @@ event_predicate=lambda e: e.name == name and e.parent_slice == None) def IterAllAsyncSlicesOfName(self, name, recursive=True): - def IsAsyncSlice(t): - return t == async_slice_module.AsyncSlice return self.IterAllEvents( recursive=recursive, - event_type_predicate=IsAsyncSlice, + event_type_predicate=self.IsAsyncSlice, event_predicate=lambda e: e.name == name) + def IterAllAsyncSlicesStartsWithName(self, name, recursive=True): + return self.IterAllEvents( + recursive=recursive, + event_type_predicate=self.IsAsyncSlice, + event_predicate=lambda e: e.name.startswith(name)) + def IterAllFlowEvents(self, recursive=True): return self.IterAllEvents( recursive=recursive,
diff --git a/tools/telemetry/telemetry/web_perf/metrics/rendering_stats.py b/tools/telemetry/telemetry/web_perf/metrics/rendering_stats.py index 75af84b..231f23e 100644 --- a/tools/telemetry/telemetry/web_perf/metrics/rendering_stats.py +++ b/tools/telemetry/telemetry/web_perf/metrics/rendering_stats.py
@@ -23,9 +23,9 @@ END_COMP_NAME = 'INPUT_EVENT_GPU_SWAP_BUFFER_COMPONENT' # Name for a main thread scroll update latency event. -SCROLL_UPDATE_EVENT_NAME = 'InputLatency:ScrollUpdate' +SCROLL_UPDATE_EVENT_NAME = 'InputLatency::ScrollUpdate' # Name for a gesture scroll update latency event. -GESTURE_SCROLL_UPDATE_EVENT_NAME = 'InputLatency:GestureScrollUpdate' +GESTURE_SCROLL_UPDATE_EVENT_NAME = 'InputLatency::GestureScrollUpdate' # These are keys used in the 'data' field dictionary located in # BenchmarkInstrumentation::ImplThreadRenderingStats. @@ -43,14 +43,14 @@ within the timeline_range. Input events dump their LatencyInfo into trace buffer as async trace event - with name "InputLatency". The trace event has a memeber 'data' containing - its latency history. + of name starting with "InputLatency". The trace event has a memeber 'data' + containing its latency history. """ input_events = [] if not process: return input_events - for event in process.IterAllAsyncSlicesOfName('InputLatency'): + for event in process.IterAllAsyncSlicesStartsWithName('InputLatency'): if event.start >= timeline_range.min and event.end <= timeline_range.max: for ss in event.sub_slices: if 'data' in ss.args:
diff --git a/tools/ubsan/blacklist.txt b/tools/ubsan/blacklist.txt index 2db4e185..0dca900 100644 --- a/tools/ubsan/blacklist.txt +++ b/tools/ubsan/blacklist.txt
@@ -107,3 +107,7 @@ # static_cast<StartPageService*> in StartPageServiceFactory::GetForProfile. type:*StartPageService* + +# Remove once function attribute level blacklisting is implemented. +# See crbug.com/476063. +fun:*forbidGCDuringConstruction*
diff --git a/tools/valgrind/gtest_exclude/content_browsertests.gtest-drmemory.txt b/tools/valgrind/gtest_exclude/content_browsertests.gtest-drmemory.txt index 98043de..55df649 100644 --- a/tools/valgrind/gtest_exclude/content_browsertests.gtest-drmemory.txt +++ b/tools/valgrind/gtest_exclude/content_browsertests.gtest-drmemory.txt
@@ -28,6 +28,7 @@ # http://crbug.com/456131 BrowserSideNavigationBrowserTest.BrowserInitiatedNavigations +BrowserSideNavigationBrowserTest.FailedNavigation # http://crbug.com/464029 WebRtcBrowserTest.CallAndModifyStream
diff --git a/ui/accessibility/ax_enums.idl b/ui/accessibility/ax_enums.idl index a61f994..43c9e4b 100644 --- a/ui/accessibility/ax_enums.idl +++ b/ui/accessibility/ax_enums.idl
@@ -313,6 +313,10 @@ // Identifies a child tree which this node hosts. child_tree_id, + // Position or Number of items in current set of listitems or treeitems + set_size, + pos_in_set, + // Indicates if a form control has invalid input or // if an element has an aria-invalid attribute. invalid_state
diff --git a/ui/accessibility/ax_node_data.cc b/ui/accessibility/ax_node_data.cc index b9a2841..99ff332 100644 --- a/ui/accessibility/ax_node_data.cc +++ b/ui/accessibility/ax_node_data.cc
@@ -242,6 +242,12 @@ break; } break; + case AX_ATTR_SET_SIZE: + result += " setsize=" + value; + break; + case AX_ATTR_POS_IN_SET: + result += " posinset=" + value; + break; case AX_ATTR_INVALID_STATE: switch (int_attributes[i].second) { case AX_INVALID_STATE_FALSE:
diff --git a/ui/accessibility/platform/ax_platform_node_win.h b/ui/accessibility/platform/ax_platform_node_win.h index af45654d6..31ca0b2 100644 --- a/ui/accessibility/platform/ax_platform_node_win.h +++ b/ui/accessibility/platform/ax_platform_node_win.h
@@ -33,7 +33,7 @@ COM_INTERFACE_ENTRY(IServiceProvider) END_COM_MAP() - virtual ~AXPlatformNodeWin(); + ~AXPlatformNodeWin() override; // AXPlatformNode overrides. void Destroy() override; @@ -48,67 +48,69 @@ // // Retrieves the child element or child object at a given point on the screen. - virtual STDMETHODIMP accHitTest(LONG x_left, LONG y_top, VARIANT* child); + STDMETHODIMP accHitTest(LONG x_left, LONG y_top, VARIANT* child) override; // Performs the object's default action. - STDMETHODIMP accDoDefaultAction(VARIANT var_id); + STDMETHODIMP accDoDefaultAction(VARIANT var_id) override; // Retrieves the specified object's current screen location. STDMETHODIMP accLocation(LONG* x_left, LONG* y_top, LONG* width, LONG* height, - VARIANT var_id); + VARIANT var_id) override; // Traverses to another UI element and retrieves the object. - STDMETHODIMP accNavigate(LONG nav_dir, VARIANT start, VARIANT* end); + STDMETHODIMP accNavigate(LONG nav_dir, VARIANT start, VARIANT* end) override; // Retrieves an IDispatch interface pointer for the specified child. - virtual STDMETHODIMP get_accChild(VARIANT var_child, IDispatch** disp_child); + STDMETHODIMP get_accChild(VARIANT var_child, IDispatch** disp_child) override; // Retrieves the number of accessible children. - virtual STDMETHODIMP get_accChildCount(LONG* child_count); + STDMETHODIMP get_accChildCount(LONG* child_count) override; // Retrieves a string that describes the object's default action. - STDMETHODIMP get_accDefaultAction(VARIANT var_id, BSTR* default_action); + STDMETHODIMP get_accDefaultAction(VARIANT var_id, + BSTR* default_action) override; // Retrieves the tooltip description. - STDMETHODIMP get_accDescription(VARIANT var_id, BSTR* desc); + STDMETHODIMP get_accDescription(VARIANT var_id, BSTR* desc) override; // Retrieves the object that has the keyboard focus. - STDMETHODIMP get_accFocus(VARIANT* focus_child); + STDMETHODIMP get_accFocus(VARIANT* focus_child) override; // Retrieves the specified object's shortcut. - STDMETHODIMP get_accKeyboardShortcut(VARIANT var_id, BSTR* access_key); + STDMETHODIMP get_accKeyboardShortcut(VARIANT var_id, + BSTR* access_key) override; // Retrieves the name of the specified object. - STDMETHODIMP get_accName(VARIANT var_id, BSTR* name); + STDMETHODIMP get_accName(VARIANT var_id, BSTR* name) override; // Retrieves the IDispatch interface of the object's parent. - STDMETHODIMP get_accParent(IDispatch** disp_parent); + STDMETHODIMP get_accParent(IDispatch** disp_parent) override; // Retrieves information describing the role of the specified object. - STDMETHODIMP get_accRole(VARIANT var_id, VARIANT* role); + STDMETHODIMP get_accRole(VARIANT var_id, VARIANT* role) override; // Retrieves the current state of the specified object. - STDMETHODIMP get_accState(VARIANT var_id, VARIANT* state); + STDMETHODIMP get_accState(VARIANT var_id, VARIANT* state) override; // Gets the help string for the specified object. - STDMETHODIMP get_accHelp(VARIANT var_id, BSTR* help); + STDMETHODIMP get_accHelp(VARIANT var_id, BSTR* help) override; // Retrieve or set the string value associated with the specified object. // Setting the value is not typically used by screen readers, but it's // used frequently by automation software. - STDMETHODIMP get_accValue(VARIANT var_id, BSTR* value); - STDMETHODIMP put_accValue(VARIANT var_id, BSTR new_value); + STDMETHODIMP get_accValue(VARIANT var_id, BSTR* value) override; + STDMETHODIMP put_accValue(VARIANT var_id, BSTR new_value) override; // IAccessible methods not implemented. - STDMETHODIMP get_accSelection(VARIANT* selected); - STDMETHODIMP accSelect(LONG flags_sel, VARIANT var_id); + STDMETHODIMP get_accSelection(VARIANT* selected) override; + STDMETHODIMP accSelect(LONG flags_sel, VARIANT var_id) override; STDMETHODIMP get_accHelpTopic(BSTR* help_file, VARIANT var_id, - LONG* topic_id); - STDMETHODIMP put_accName(VARIANT var_id, BSTR put_name); + LONG* topic_id) override; + STDMETHODIMP put_accName(VARIANT var_id, BSTR put_name) override; // // IAccessible2 methods. @@ -282,7 +284,9 @@ // IServiceProvider methods. // - STDMETHODIMP QueryService(REFGUID guidService, REFIID riid, void** object); + STDMETHODIMP QueryService(REFGUID guidService, + REFIID riid, + void** object) override; protected: AXPlatformNodeWin();
diff --git a/ui/aura/input_state_lookup_win.h b/ui/aura/input_state_lookup_win.h index 94e4ee61..04741fd 100644 --- a/ui/aura/input_state_lookup_win.h +++ b/ui/aura/input_state_lookup_win.h
@@ -15,10 +15,10 @@ class AURA_EXPORT InputStateLookupWin : public InputStateLookup { public: InputStateLookupWin(); - virtual ~InputStateLookupWin(); + ~InputStateLookupWin() override; // InputStateLookup overrides: - virtual bool IsMouseButtonDown() const override; + bool IsMouseButtonDown() const override; private: DISALLOW_COPY_AND_ASSIGN(InputStateLookupWin);
diff --git a/ui/aura/remote_window_tree_host_win.h b/ui/aura/remote_window_tree_host_win.h index b680cc87..4cde32c9 100644 --- a/ui/aura/remote_window_tree_host_win.h +++ b/ui/aura/remote_window_tree_host_win.h
@@ -79,7 +79,7 @@ protected: RemoteWindowTreeHostWin(); - virtual ~RemoteWindowTreeHostWin(); + ~RemoteWindowTreeHostWin() override; private: // IPC message handing methods: @@ -116,25 +116,25 @@ void OnImeInputSourceChanged(uint16 language_id, bool is_ime); // WindowTreeHost overrides: - virtual ui::EventSource* GetEventSource() override; - virtual gfx::AcceleratedWidget GetAcceleratedWidget() override; - virtual void Show() override; - virtual void Hide() override; - virtual gfx::Rect GetBounds() const override; - virtual void SetBounds(const gfx::Rect& bounds) override; - virtual gfx::Point GetLocationOnNativeScreen() const override; - virtual void SetCapture() override; - virtual void ReleaseCapture() override; - virtual void SetCursorNative(gfx::NativeCursor cursor) override; - virtual void MoveCursorToNative(const gfx::Point& location) override; - virtual void OnCursorVisibilityChangedNative(bool show) override; + ui::EventSource* GetEventSource() override; + gfx::AcceleratedWidget GetAcceleratedWidget() override; + void Show() override; + void Hide() override; + gfx::Rect GetBounds() const override; + void SetBounds(const gfx::Rect& bounds) override; + gfx::Point GetLocationOnNativeScreen() const override; + void SetCapture() override; + void ReleaseCapture() override; + void SetCursorNative(gfx::NativeCursor cursor) override; + void MoveCursorToNative(const gfx::Point& location) override; + void OnCursorVisibilityChangedNative(bool show) override; // ui::EventSource: - virtual ui::EventProcessor* GetEventProcessor() override; + ui::EventProcessor* GetEventProcessor() override; // ui::internal::RemoteInputMethodDelegateWin overrides: - virtual void CancelComposition() override; - virtual void OnTextInputClientUpdated( + void CancelComposition() override; + void OnTextInputClientUpdated( const std::vector<int32>& input_scopes, const std::vector<gfx::Rect>& composition_character_bounds) override;
diff --git a/ui/aura/test/ui_controls_factory_aurawin.cc b/ui/aura/test/ui_controls_factory_aurawin.cc index 717aea5e..36953eb9 100644 --- a/ui/aura/test/ui_controls_factory_aurawin.cc +++ b/ui/aura/test/ui_controls_factory_aurawin.cc
@@ -30,50 +30,51 @@ UIControlsWin() {} // UIControlsAura overrides: - virtual bool SendKeyPress(gfx::NativeWindow native_window, - ui::KeyboardCode key, - bool control, - bool shift, - bool alt, - bool command) { + bool SendKeyPress(gfx::NativeWindow native_window, + ui::KeyboardCode key, + bool control, + bool shift, + bool alt, + bool command) override { DCHECK(!command); // No command key on Aura HWND window = native_window->GetHost()->GetAcceleratedWidget(); return SendKeyPressImpl( window, key, control, shift, alt, base::Closure()); } - virtual bool SendKeyPressNotifyWhenDone(gfx::NativeWindow native_window, - ui::KeyboardCode key, - bool control, - bool shift, - bool alt, - bool command, - const base::Closure& task) { + bool SendKeyPressNotifyWhenDone(gfx::NativeWindow native_window, + ui::KeyboardCode key, + bool control, + bool shift, + bool alt, + bool command, + const base::Closure& task) override { DCHECK(!command); // No command key on Aura HWND window = native_window->GetHost()->GetAcceleratedWidget(); return SendKeyPressImpl(window, key, control, shift, alt, task); } - virtual bool SendMouseMove(long screen_x, long screen_y) { + bool SendMouseMove(long screen_x, long screen_y) override { return SendMouseMoveImpl(screen_x, screen_y, base::Closure()); } - virtual bool SendMouseMoveNotifyWhenDone(long screen_x, - long screen_y, - const base::Closure& task) { + bool SendMouseMoveNotifyWhenDone(long screen_x, + long screen_y, + const base::Closure& task) override { return SendMouseMoveImpl(screen_x, screen_y, task); } - virtual bool SendMouseEvents(MouseButton type, int state) { + bool SendMouseEvents(MouseButton type, int state) override { return SendMouseEventsImpl(type, state, base::Closure()); } - virtual bool SendMouseEventsNotifyWhenDone(MouseButton type, - int state, - const base::Closure& task) { + bool SendMouseEventsNotifyWhenDone(MouseButton type, + int state, + const base::Closure& task) override { return SendMouseEventsImpl(type, state, task); } - virtual bool SendMouseClick(MouseButton type) { + bool SendMouseClick(MouseButton type) override { return SendMouseEvents(type, UP | DOWN); } - virtual void RunClosureAfterAllPendingUIEvents(const base::Closure& closure) { + void RunClosureAfterAllPendingUIEvents( + const base::Closure& closure) override { // On windows, posting UI events is synchronous so just post the closure. base::MessageLoopForUI::current()->PostTask(FROM_HERE, closure); }
diff --git a/ui/aura/window_tree_host_win.h b/ui/aura/window_tree_host_win.h index a448a3f..8399c8e 100644 --- a/ui/aura/window_tree_host_win.h +++ b/ui/aura/window_tree_host_win.h
@@ -21,40 +21,39 @@ public NON_EXPORTED_BASE(ui::PlatformWindowDelegate) { public: explicit WindowTreeHostWin(const gfx::Rect& bounds); - virtual ~WindowTreeHostWin(); + ~WindowTreeHostWin() override; // WindowTreeHost: - virtual ui::EventSource* GetEventSource() override; - virtual gfx::AcceleratedWidget GetAcceleratedWidget() override; - virtual void Show() override; - virtual void Hide() override; - virtual gfx::Rect GetBounds() const override; - virtual void SetBounds(const gfx::Rect& bounds) override; - virtual gfx::Point GetLocationOnNativeScreen() const override; - virtual void SetCapture() override; - virtual void ReleaseCapture() override; - virtual void SetCursorNative(gfx::NativeCursor cursor) override; - virtual void MoveCursorToNative(const gfx::Point& location) override; - virtual void OnCursorVisibilityChangedNative(bool show) override; + ui::EventSource* GetEventSource() override; + gfx::AcceleratedWidget GetAcceleratedWidget() override; + void Show() override; + void Hide() override; + gfx::Rect GetBounds() const override; + void SetBounds(const gfx::Rect& bounds) override; + gfx::Point GetLocationOnNativeScreen() const override; + void SetCapture() override; + void ReleaseCapture() override; + void SetCursorNative(gfx::NativeCursor cursor) override; + void MoveCursorToNative(const gfx::Point& location) override; + void OnCursorVisibilityChangedNative(bool show) override; // ui::EventSource: - virtual ui::EventProcessor* GetEventProcessor() override; + ui::EventProcessor* GetEventProcessor() override; protected: gfx::AcceleratedWidget hwnd() const { return widget_; } private: // ui::PlatformWindowDelegate: - virtual void OnBoundsChanged(const gfx::Rect& new_bounds) override; - virtual void OnDamageRect(const gfx::Rect& damaged_region) override; - virtual void DispatchEvent(ui::Event* event) override; - virtual void OnCloseRequest() override; - virtual void OnClosed() override; - virtual void OnWindowStateChanged(ui::PlatformWindowState new_state) override; - virtual void OnLostCapture() override; - virtual void OnAcceleratedWidgetAvailable( - gfx::AcceleratedWidget widget) override; - virtual void OnActivationChanged(bool active) override; + void OnBoundsChanged(const gfx::Rect& new_bounds) override; + void OnDamageRect(const gfx::Rect& damaged_region) override; + void DispatchEvent(ui::Event* event) override; + void OnCloseRequest() override; + void OnClosed() override; + void OnWindowStateChanged(ui::PlatformWindowState new_state) override; + void OnLostCapture() override; + void OnAcceleratedWidgetAvailable(gfx::AcceleratedWidget widget) override; + void OnActivationChanged(bool active) override; bool has_capture_; gfx::Rect bounds_;
diff --git a/ui/base/ime/chromeos/ime_bridge.h b/ui/base/ime/chromeos/ime_bridge.h index 3978ec4..09dd33f3 100644 --- a/ui/base/ime/chromeos/ime_bridge.h +++ b/ui/base/ime/chromeos/ime_bridge.h
@@ -106,6 +106,10 @@ // Called when the composition bounds changed. virtual void SetCompositionBounds(const std::vector<gfx::Rect>& bounds) = 0; + // Returns whether the engine is interested in key events. + // If not, InputMethodChromeOS won't feed it with key events. + virtual bool IsInterestedInKeyEvent() const = 0; + protected: IMEEngineHandlerInterface() {} };
diff --git a/ui/base/ime/chromeos/mock_ime_engine_handler.cc b/ui/base/ime/chromeos/mock_ime_engine_handler.cc index 5645494..867ef19 100644 --- a/ui/base/ime/chromeos/mock_ime_engine_handler.cc +++ b/ui/base/ime/chromeos/mock_ime_engine_handler.cc
@@ -49,6 +49,10 @@ ++reset_call_count_; } +bool MockIMEEngineHandler::IsInterestedInKeyEvent() const { + return true; +} + void MockIMEEngineHandler::ProcessKeyEvent( const ui::KeyEvent& key_event, const KeyEventDoneCallback& callback) {
diff --git a/ui/base/ime/chromeos/mock_ime_engine_handler.h b/ui/base/ime/chromeos/mock_ime_engine_handler.h index b37a62f..18d3d99 100644 --- a/ui/base/ime/chromeos/mock_ime_engine_handler.h +++ b/ui/base/ime/chromeos/mock_ime_engine_handler.h
@@ -23,6 +23,7 @@ void Disable() override; void PropertyActivate(const std::string& property_name) override; void Reset() override; + bool IsInterestedInKeyEvent() const override; void ProcessKeyEvent(const ui::KeyEvent& key_event, const KeyEventDoneCallback& callback) override; void CandidateClicked(uint32 index) override;
diff --git a/ui/base/ime/input_method_base.cc b/ui/base/ime/input_method_base.cc index 0dca378..dd11947 100644 --- a/ui/base/ime/input_method_base.cc +++ b/ui/base/ime/input_method_base.cc
@@ -38,12 +38,10 @@ } void InputMethodBase::OnFocus() { - DCHECK(!system_toplevel_window_focused_); system_toplevel_window_focused_ = true; } void InputMethodBase::OnBlur() { - DCHECK(system_toplevel_window_focused_); system_toplevel_window_focused_ = false; }
diff --git a/ui/base/ime/input_method_chromeos.cc b/ui/base/ime/input_method_chromeos.cc index 3a6db87..071a2df 100644 --- a/ui/base/ime/input_method_chromeos.cc +++ b/ui/base/ime/input_method_chromeos.cc
@@ -37,7 +37,6 @@ internal::InputMethodDelegate* delegate) : composing_text_(false), composition_changed_(false), - current_keyevent_id_(0), weak_ptr_factory_(this) { SetDelegate(delegate); chromeos::IMEBridge::Get()->SetInputContextHandler(this); @@ -46,7 +45,6 @@ } InputMethodChromeOS::~InputMethodChromeOS() { - AbandonAllPendingKeyEvents(); ConfirmCompositionText(); // We are dead, so we need to ask the client to stop relying on us. OnInputMethodChanged(); @@ -71,12 +69,8 @@ return false; } -void InputMethodChromeOS::ProcessKeyEventDone(uint32 id, - ui::KeyEvent* event, +void InputMethodChromeOS::ProcessKeyEventDone(const ui::KeyEvent* event, bool is_handled) { - if (pending_key_events_.find(id) == pending_key_events_.end()) - return; // Abandoned key event. - DCHECK(event); if (event->type() == ET_KEY_PRESSED) { if (is_handled) { @@ -92,9 +86,6 @@ if (event->type() == ET_KEY_PRESSED || event->type() == ET_KEY_RELEASED) ProcessKeyEventPostIME(*event, is_handled); - - // ProcessKeyEventPostIME may change the |pending_key_events_|. - pending_key_events_.erase(id); } bool InputMethodChromeOS::DispatchKeyEvent(const ui::KeyEvent& event) { @@ -138,18 +129,16 @@ return true; } - pending_key_events_.insert(current_keyevent_id_); - - ui::KeyEvent* copied_event = new ui::KeyEvent(event); - GetEngine()->ProcessKeyEvent( - event, - base::Bind(&InputMethodChromeOS::ProcessKeyEventDone, - weak_ptr_factory_.GetWeakPtr(), - current_keyevent_id_, - // Pass the ownership of |copied_event|. - base::Owned(copied_event))); - - ++current_keyevent_id_; + if (GetEngine()->IsInterestedInKeyEvent()) { + GetEngine()->ProcessKeyEvent( + event, + base::Bind(&InputMethodChromeOS::ProcessKeyEventDone, + weak_ptr_factory_.GetWeakPtr(), + // Pass the ownership of the new copied event. + base::Owned(new ui::KeyEvent(event)))); + } else { + ProcessKeyEventDone(&event, false); + } return true; } @@ -312,11 +301,6 @@ composing_text_ = false; composition_changed_ = false; - // We need to abandon all pending key events, but as above comment says, there - // is no reliable way to abandon all results generated by these abandoned key - // events. - AbandonAllPendingKeyEvents(); - // This function runs asynchronously. // Note: some input method engines may not support reset method, such as // ibus-anthy. But as we control all input method engines by ourselves, we can @@ -469,10 +453,6 @@ DispatchKeyEventPostIME(evt); } -void InputMethodChromeOS::AbandonAllPendingKeyEvents() { - pending_key_events_.clear(); -} - void InputMethodChromeOS::CommitText(const std::string& text) { if (text.empty()) return; @@ -493,7 +473,7 @@ // If we are not handling key event, do not bother sending text result if the // focused text input client does not support text input. - if (pending_key_events_.empty() && !IsTextInputTypeNone()) { + if (!IsTextInputTypeNone()) { SendFakeProcessKeyEvent(true); GetTextInputClient()->InsertText(utf16_text); SendFakeProcessKeyEvent(false); @@ -537,13 +517,11 @@ // If we receive a composition text without pending key event, then we need to // send it to the focused text input client directly. - if (pending_key_events_.empty()) { - SendFakeProcessKeyEvent(true); - GetTextInputClient()->SetCompositionText(composition_); - SendFakeProcessKeyEvent(false); - composition_changed_ = false; - composition_.Clear(); - } + SendFakeProcessKeyEvent(true); + GetTextInputClient()->SetCompositionText(composition_); + SendFakeProcessKeyEvent(false); + composition_changed_ = false; + composition_.Clear(); } void InputMethodChromeOS::HidePreeditText() { @@ -554,15 +532,13 @@ composition_changed_ = true; composition_.Clear(); - if (pending_key_events_.empty()) { - TextInputClient* client = GetTextInputClient(); - if (client && client->HasCompositionText()) { - SendFakeProcessKeyEvent(true); - client->ClearCompositionText(); - SendFakeProcessKeyEvent(false); - } - composition_changed_ = false; + TextInputClient* client = GetTextInputClient(); + if (client && client->HasCompositionText()) { + SendFakeProcessKeyEvent(true); + client->ClearCompositionText(); + SendFakeProcessKeyEvent(false); } + composition_changed_ = false; } void InputMethodChromeOS::DeleteSurroundingText(int32 offset, uint32 length) {
diff --git a/ui/base/ime/input_method_chromeos.h b/ui/base/ime/input_method_chromeos.h index f0912234..98794165 100644 --- a/ui/base/ime/input_method_chromeos.h +++ b/ui/base/ime/input_method_chromeos.h
@@ -91,10 +91,6 @@ // Sends a fake key event for IME composing without physical key events. void SendFakeProcessKeyEvent(bool pressed) const; - // Abandons all pending key events. It usually happends when we lose keyboard - // focus, the text input type is changed or we are destroyed. - void AbandonAllPendingKeyEvents(); - // Passes keyevent and executes character composition if necessary. Returns // true if character composer comsumes key event. bool ExecuteCharacterComposer(const ui::KeyEvent& event); @@ -110,7 +106,7 @@ void HidePreeditText(); // Callback function for IMEEngineHandlerInterface::ProcessKeyEvent. - void ProcessKeyEventDone(uint32 id, ui::KeyEvent* event, bool is_handled); + void ProcessKeyEventDone(const ui::KeyEvent* event, bool is_handled); // Returns whether an non-password input field is focused. bool IsNonPasswordInputFieldFocused(); @@ -118,11 +114,6 @@ // Returns true if an text input field is focused. bool IsInputFieldFocused(); - // All pending key events. Note: we do not own these object, we just save - // pointers to these object so that we can abandon them when necessary. - // They will be deleted in ProcessKeyEventDone(). - std::set<uint32> pending_key_events_; - // Pending composition text generated by the current pending key event. // It'll be sent to the focused text input client as soon as we receive the // processing result of the pending key event. @@ -142,9 +133,6 @@ // Indicates if the composition text is changed or deleted. bool composition_changed_; - // The latest id of key event. - uint32 current_keyevent_id_; - // An object to compose a character from a sequence of key presses // including dead key etc. CharacterComposer character_composer_;
diff --git a/ui/base/ime/input_method_chromeos_unittest.cc b/ui/base/ime/input_method_chromeos_unittest.cc index 4562eb4..f2d16319 100644 --- a/ui/base/ime/input_method_chromeos_unittest.cc +++ b/ui/base/ime/input_method_chromeos_unittest.cc
@@ -1008,27 +1008,6 @@ EXPECT_FALSE(ime_->process_key_event_post_ime_args().handled); } -TEST_F(InputMethodChromeOSKeyEventTest, KeyEventDelayResponseResetTest) { - ScopedXI2Event xevent; - xevent.InitKeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_A, ui::EF_SHIFT_DOWN); - const ui::KeyEvent event(xevent); - - // Do key event. - input_type_ = TEXT_INPUT_TYPE_TEXT; - ime_->OnTextInputTypeChanged(this); - ime_->DispatchKeyEvent(event); - - // Check before state. - EXPECT_EQ(1, mock_ime_engine_handler_->process_key_event_call_count()); - EXPECT_EQ(0, ime_->process_key_event_post_ime_call_count()); - - ime_->ResetContext(); - - // Do callback. - mock_ime_engine_handler_->last_passed_callback().Run(true); - - EXPECT_EQ(0, ime_->process_key_event_post_ime_call_count()); -} // TODO(nona): Introduce ProcessKeyEventPostIME tests(crbug.com/156593). } // namespace ui
diff --git a/ui/base/ios/OWNERS b/ui/base/ios/OWNERS index f2fff55..ae82009 100644 --- a/ui/base/ios/OWNERS +++ b/ui/base/ios/OWNERS
@@ -1,3 +1,2 @@ -lliabraa@chromium.org rohitrao@chromium.org sdefresne@chromium.org
diff --git a/ui/compositor/compositor_switches.cc b/ui/compositor/compositor_switches.cc index f003e9d..7b8fed3 100644 --- a/ui/compositor/compositor_switches.cc +++ b/ui/compositor/compositor_switches.cc
@@ -20,7 +20,7 @@ const char kUIEnableCompositorAnimationTimelines[] = "ui-enable-compositor-animation-timelines"; -const char kUIDisableImplSidePainting[] = "ui-disable-impl-side-painting"; +const char kUIEnableImplSidePainting[] = "ui-enable-impl-side-painting"; const char kUIEnableSlimmingPaint[] = "ui-enable-slimming-paint"; @@ -35,8 +35,7 @@ bool IsUIImplSidePaintingEnabled() { const base::CommandLine& command_line = *base::CommandLine::ForCurrentProcess(); - - return !command_line.HasSwitch(switches::kUIDisableImplSidePainting); + return command_line.HasSwitch(switches::kUIEnableImplSidePainting); } bool IsUIZeroCopyEnabled() {
diff --git a/ui/compositor/compositor_switches.h b/ui/compositor/compositor_switches.h index 2812d62..a6ffba8 100644 --- a/ui/compositor/compositor_switches.h +++ b/ui/compositor/compositor_switches.h
@@ -13,8 +13,8 @@ COMPOSITOR_EXPORT extern const char kEnablePixelOutputInTests[]; COMPOSITOR_EXPORT extern const char kUIDisableThreadedCompositing[]; COMPOSITOR_EXPORT extern const char kUIEnableCompositorAnimationTimelines[]; +COMPOSITOR_EXPORT extern const char kUIEnableImplSidePainting[]; COMPOSITOR_EXPORT extern const char kUIEnableSlimmingPaint[]; -COMPOSITOR_EXPORT extern const char kUIDisableImplSidePainting[]; COMPOSITOR_EXPORT extern const char kUIEnableZeroCopy[]; COMPOSITOR_EXPORT extern const char kUIShowPaintRects[];
diff --git a/ui/compositor/compositor_unittest.cc b/ui/compositor/compositor_unittest.cc index c64b8ce..3662080 100644 --- a/ui/compositor/compositor_unittest.cc +++ b/ui/compositor/compositor_unittest.cc
@@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "base/message_loop/message_loop_proxy.h" #include "base/run_loop.h" +#include "base/thread_task_runner_handle.h" #include "cc/output/begin_frame_args.h" #include "cc/test/begin_frame_args_test.h" #include "testing/gmock/include/gmock/gmock.h" @@ -30,7 +30,7 @@ ~CompositorTest() override {} void SetUp() override { - task_runner_ = base::MessageLoopProxy::current(); + task_runner_ = base::ThreadTaskRunnerHandle::Get(); ui::ContextFactory* context_factory = ui::InitializeContextFactoryForTests(false); @@ -44,11 +44,11 @@ } protected: - base::MessageLoopProxy* task_runner() { return task_runner_.get(); } + base::SingleThreadTaskRunner* task_runner() { return task_runner_.get(); } ui::Compositor* compositor() { return compositor_.get(); } private: - scoped_refptr<base::MessageLoopProxy> task_runner_; + scoped_refptr<base::SingleThreadTaskRunner> task_runner_; scoped_ptr<ui::Compositor> compositor_; DISALLOW_COPY_AND_ASSIGN(CompositorTest);
diff --git a/ui/compositor/test/test_compositor_host_win.cc b/ui/compositor/test/test_compositor_host_win.cc index e89d092..3cdd2835 100644 --- a/ui/compositor/test/test_compositor_host_win.cc +++ b/ui/compositor/test/test_compositor_host_win.cc
@@ -24,17 +24,11 @@ compositor_->SetScaleAndSize(1.0f, GetSize()); } - virtual ~TestCompositorHostWin() { - DestroyWindow(hwnd()); - } + ~TestCompositorHostWin() override { DestroyWindow(hwnd()); } // Overridden from TestCompositorHost: - virtual void Show() override { - ShowWindow(hwnd(), SW_SHOWNORMAL); - } - virtual ui::Compositor* GetCompositor() override { - return compositor_.get(); - } + void Show() override { ShowWindow(hwnd(), SW_SHOWNORMAL); } + ui::Compositor* GetCompositor() override { return compositor_.get(); } private: CR_BEGIN_MSG_MAP_EX(TestCompositorHostWin)
diff --git a/ui/display/BUILD.gn b/ui/display/BUILD.gn index 9b5f2ba..d3dcaa6 100644 --- a/ui/display/BUILD.gn +++ b/ui/display/BUILD.gn
@@ -7,13 +7,18 @@ component("display") { sources = [ + "chromeos/apply_content_protection_task.cc", + "chromeos/apply_content_protection_task.h", "chromeos/configure_displays_task.cc", "chromeos/configure_displays_task.h", "chromeos/display_configurator.cc", "chromeos/display_configurator.h", + "chromeos/display_layout_manager.h", "chromeos/display_util.cc", "chromeos/display_util.h", "chromeos/ozone/display_configurator_ozone.cc", + "chromeos/query_content_protection_task.cc", + "chromeos/query_content_protection_task.h", "chromeos/update_display_configuration_task.cc", "chromeos/update_display_configuration_task.h", "chromeos/x11/display_configurator_x11.cc", @@ -111,6 +116,8 @@ "chromeos/test/action_logger.h", "chromeos/test/action_logger_util.cc", "chromeos/test/action_logger_util.h", + "chromeos/test/test_display_layout_manager.cc", + "chromeos/test/test_display_layout_manager.h", "chromeos/test/test_native_display_delegate.cc", "chromeos/test/test_native_display_delegate.h", ] @@ -128,8 +135,10 @@ test("display_unittests") { sources = [ + "chromeos/apply_content_protection_task_unittest.cc", "chromeos/configure_displays_task_unittest.cc", "chromeos/display_configurator_unittest.cc", + "chromeos/query_content_protection_task_unittest.cc", "chromeos/update_display_configuration_task_unittest.cc", "chromeos/x11/display_util_x11_unittest.cc", "chromeos/x11/native_display_event_dispatcher_x11_unittest.cc",
diff --git a/ui/display/chromeos/apply_content_protection_task.cc b/ui/display/chromeos/apply_content_protection_task.cc new file mode 100644 index 0000000..e25d427 --- /dev/null +++ b/ui/display/chromeos/apply_content_protection_task.cc
@@ -0,0 +1,172 @@ +// 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/display/chromeos/apply_content_protection_task.h" + +#include "ui/display/chromeos/display_layout_manager.h" +#include "ui/display/types/display_snapshot.h" +#include "ui/display/types/native_display_delegate.h" + +namespace ui { + +namespace { + +bool GetHDCPCapableDisplays( + const DisplayLayoutManager& layout_manager, + std::vector<DisplaySnapshot*>* hdcp_capable_displays) { + for (DisplaySnapshot* display : layout_manager.GetDisplayStates()) { + switch (display->type()) { + case DISPLAY_CONNECTION_TYPE_UNKNOWN: + return false; + // DisplayPort, DVI, and HDMI all support HDCP. + case DISPLAY_CONNECTION_TYPE_DISPLAYPORT: + case DISPLAY_CONNECTION_TYPE_DVI: + case DISPLAY_CONNECTION_TYPE_HDMI: + hdcp_capable_displays->push_back(display); + break; + case DISPLAY_CONNECTION_TYPE_INTERNAL: + case DISPLAY_CONNECTION_TYPE_VGA: + case DISPLAY_CONNECTION_TYPE_NETWORK: + // No protections for these types. Do nothing. + break; + case DISPLAY_CONNECTION_TYPE_NONE: + NOTREACHED(); + break; + } + } + + return true; +} + +} // namespace + +ApplyContentProtectionTask::ApplyContentProtectionTask( + DisplayLayoutManager* layout_manager, + NativeDisplayDelegate* native_display_delegate, + const DisplayConfigurator::ContentProtections& requests, + const ResponseCallback& callback) + : layout_manager_(layout_manager), + native_display_delegate_(native_display_delegate), + requests_(requests), + callback_(callback), + query_status_(true), + pending_requests_(0), + weak_ptr_factory_(this) { +} + +ApplyContentProtectionTask::~ApplyContentProtectionTask() { +} + +void ApplyContentProtectionTask::Run() { + std::vector<DisplaySnapshot*> hdcp_capable_displays; + if (!GetHDCPCapableDisplays(*layout_manager_, &hdcp_capable_displays)) { + callback_.Run(false); + return; + } + + pending_requests_ = hdcp_capable_displays.size(); + if (pending_requests_ == 0) { + callback_.Run(true); + return; + } + + // Need to poll the driver for updates since other applications may have + // updated the state. + for (DisplaySnapshot* display : hdcp_capable_displays) { + native_display_delegate_->GetHDCPState( + *display, + base::Bind(&ApplyContentProtectionTask::OnHDCPStateUpdate, + weak_ptr_factory_.GetWeakPtr(), display->display_id())); + } +} + +void ApplyContentProtectionTask::OnHDCPStateUpdate(int64_t display_id, + bool success, + HDCPState state) { + query_status_ &= success; + display_hdcp_state_map_[display_id] = state; + pending_requests_--; + + // Wait for all the requests before continuing. + if (pending_requests_ != 0) + return; + + if (!query_status_) { + callback_.Run(false); + return; + } + + ApplyProtections(); +} + +void ApplyContentProtectionTask::ApplyProtections() { + std::vector<DisplaySnapshot*> hdcp_capable_displays; + if (!GetHDCPCapableDisplays(*layout_manager_, &hdcp_capable_displays)) { + callback_.Run(false); + return; + } + + std::vector<std::pair<DisplaySnapshot*, HDCPState>> hdcp_requests; + // Figure out which displays need to have their HDCP state changed. + for (DisplaySnapshot* display : hdcp_capable_displays) { + uint32_t desired_mask = GetDesiredProtectionMask(display->display_id()); + + auto it = display_hdcp_state_map_.find(display->display_id()); + // If the display can't be found, the display configuration changed. + if (it == display_hdcp_state_map_.end()) { + callback_.Run(false); + return; + } + + bool hdcp_enabled = it->second != HDCP_STATE_UNDESIRED; + bool hdcp_requested = desired_mask & CONTENT_PROTECTION_METHOD_HDCP; + if (hdcp_enabled != hdcp_requested) { + hdcp_requests.push_back(std::make_pair( + display, hdcp_requested ? HDCP_STATE_DESIRED : HDCP_STATE_UNDESIRED)); + } + } + + pending_requests_ = hdcp_requests.size(); + // All the requested changes are the same as the current HDCP state. Nothing + // to do anymore, just ack the content protection change. + if (pending_requests_ == 0) { + callback_.Run(true); + return; + } + + for (const auto& pair : hdcp_requests) { + native_display_delegate_->SetHDCPState( + *pair.first, pair.second, + base::Bind(&ApplyContentProtectionTask::OnHDCPStateApplied, + weak_ptr_factory_.GetWeakPtr())); + } +} + +void ApplyContentProtectionTask::OnHDCPStateApplied(bool success) { + query_status_ &= success; + pending_requests_--; + + if (pending_requests_ == 0) + callback_.Run(query_status_); +} + +uint32_t ApplyContentProtectionTask::GetDesiredProtectionMask( + int64_t display_id) const { + uint32_t desired_mask = 0; + // In mirror mode, protection request of all displays need to be fulfilled. + // In non-mirror mode, only request of client's display needs to be + // fulfilled. + if (layout_manager_->IsMirroring()) { + for (auto pair : requests_) + desired_mask |= pair.second; + } else { + auto it = requests_.find(display_id); + if (it != requests_.end()) + desired_mask = it->second; + } + + return desired_mask; +} + +} // namespace ui
diff --git a/ui/display/chromeos/apply_content_protection_task.h b/ui/display/chromeos/apply_content_protection_task.h new file mode 100644 index 0000000..48fa59f --- /dev/null +++ b/ui/display/chromeos/apply_content_protection_task.h
@@ -0,0 +1,85 @@ +// 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_DISPLAY_CHROMEOS_APPLY_CONTENT_PROTECTION_TASK_H_ +#define UI_DISPLAY_CHROMEOS_APPLY_CONTENT_PROTECTION_TASK_H_ + +#include <map> +#include <vector> + +#include "base/memory/weak_ptr.h" +#include "ui/display/chromeos/display_configurator.h" + +namespace ui { + +class DisplayLayoutManager; +class DisplaySnapshot; +class NativeDisplayDelegate; + +// In order to apply content protection the task executes in the following +// manner: +// 1) Run() +// a) Query NativeDisplayDelegate for HDCP state on capable displays +// b) OnHDCPStateUpdate() called for each display as response to (a) +// 2) ApplyProtections() +// a) Compute preferred HDCP state for capable displays +// b) Call into NativeDisplayDelegate to set HDCP state on capable displays +// c) OnHDCPStateApplied() called for each display as reponse to (b) +// 3) Call |callback_| to signal end of task. +// +// Note, in steps 1a and 2a, if no HDCP capable displays are found or if errors +// are reported, the task finishes early and skips to step 3. +class DISPLAY_EXPORT ApplyContentProtectionTask { + public: + typedef base::Callback<void(bool)> ResponseCallback; + + ApplyContentProtectionTask( + DisplayLayoutManager* layout_manager, + NativeDisplayDelegate* native_display_delegate, + const DisplayConfigurator::ContentProtections& requests, + const ResponseCallback& callback); + ~ApplyContentProtectionTask(); + + void Run(); + + private: + // Callback to NatvieDisplayDelegate::GetHDCPState() + void OnHDCPStateUpdate(int64_t display_id, bool success, HDCPState state); + + // Callback to NativeDisplayDelegate::SetHDCPState() + void OnHDCPStateApplied(bool success); + + void ApplyProtections(); + + uint32_t GetDesiredProtectionMask(int64_t display_id) const; + + DisplayLayoutManager* layout_manager_; // Not owned. + + NativeDisplayDelegate* native_display_delegate_; // Not owned. + + DisplayConfigurator::ContentProtections requests_; + + // Callback used to respond once the task finishes. + ResponseCallback callback_; + + // Mapping between display IDs and the HDCP state returned by + // NativeDisplayDelegate. + std::map<int64_t, HDCPState> display_hdcp_state_map_; + + // Tracks the status of the NativeDisplayDelegate responses. This will be true + // if all the queries were successful, false otherwise. + bool query_status_; + + // Tracks the number of NativeDisplayDelegate requests sent but not answered + // yet. + size_t pending_requests_; + + base::WeakPtrFactory<ApplyContentProtectionTask> weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(ApplyContentProtectionTask); +}; + +} // namespace ui + +#endif // UI_DISPLAY_CHROMEOS_APPLY_CONTENT_PROTECTION_TASK_H_
diff --git a/ui/display/chromeos/apply_content_protection_task_unittest.cc b/ui/display/chromeos/apply_content_protection_task_unittest.cc new file mode 100644 index 0000000..df279c9 --- /dev/null +++ b/ui/display/chromeos/apply_content_protection_task_unittest.cc
@@ -0,0 +1,174 @@ +// 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/bind.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/display/chromeos/apply_content_protection_task.h" +#include "ui/display/chromeos/display_layout_manager.h" +#include "ui/display/chromeos/test/action_logger_util.h" +#include "ui/display/chromeos/test/test_display_layout_manager.h" +#include "ui/display/chromeos/test/test_display_snapshot.h" +#include "ui/display/chromeos/test/test_native_display_delegate.h" + +namespace ui { +namespace test { + +namespace { + +scoped_ptr<DisplaySnapshot> CreateDisplaySnapshot(int64_t id, + DisplayConnectionType type) { + scoped_ptr<TestDisplaySnapshot> display(new TestDisplaySnapshot()); + display->set_display_id(id); + display->set_type(type); + + return display.Pass(); +} + +} // namespace + +class ApplyContentProtectionTaskTest : public testing::Test { + public: + enum Response { + ERROR, + SUCCESS, + NOT_CALLED, + }; + + ApplyContentProtectionTaskTest() + : response_(NOT_CALLED), display_delegate_(&log_) {} + ~ApplyContentProtectionTaskTest() override {} + + void ResponseCallback(bool success) { response_ = success ? SUCCESS : ERROR; } + + protected: + Response response_; + ActionLogger log_; + TestNativeDisplayDelegate display_delegate_; + + private: + DISALLOW_COPY_AND_ASSIGN(ApplyContentProtectionTaskTest); +}; + +TEST_F(ApplyContentProtectionTaskTest, ApplyWithNoHDCPCapableDisplay) { + ScopedVector<DisplaySnapshot> displays; + displays.push_back( + CreateDisplaySnapshot(1, DISPLAY_CONNECTION_TYPE_INTERNAL)); + TestDisplayLayoutManager layout_manager(displays.Pass(), + MULTIPLE_DISPLAY_STATE_SINGLE); + + DisplayConfigurator::ContentProtections request; + request[1] = CONTENT_PROTECTION_METHOD_HDCP; + ApplyContentProtectionTask task( + &layout_manager, &display_delegate_, request, + base::Bind(&ApplyContentProtectionTaskTest::ResponseCallback, + base::Unretained(this))); + task.Run(); + + EXPECT_EQ(SUCCESS, response_); + EXPECT_EQ(kNoActions, log_.GetActionsAndClear()); +} + +TEST_F(ApplyContentProtectionTaskTest, ApplyWithHDMIDisplay) { + ScopedVector<DisplaySnapshot> displays; + displays.push_back(CreateDisplaySnapshot(1, DISPLAY_CONNECTION_TYPE_HDMI)); + TestDisplayLayoutManager layout_manager(displays.Pass(), + MULTIPLE_DISPLAY_STATE_SINGLE); + + DisplayConfigurator::ContentProtections request; + request[1] = CONTENT_PROTECTION_METHOD_HDCP; + ApplyContentProtectionTask task( + &layout_manager, &display_delegate_, request, + base::Bind(&ApplyContentProtectionTaskTest::ResponseCallback, + base::Unretained(this))); + task.Run(); + + EXPECT_EQ(SUCCESS, response_); + EXPECT_EQ( + JoinActions(GetSetHDCPStateAction(*layout_manager.GetDisplayStates()[0], + HDCP_STATE_DESIRED).c_str(), + NULL), + log_.GetActionsAndClear()); +} + +TEST_F(ApplyContentProtectionTaskTest, ApplyWithUnknownDisplay) { + ScopedVector<DisplaySnapshot> displays; + displays.push_back(CreateDisplaySnapshot(1, DISPLAY_CONNECTION_TYPE_UNKNOWN)); + TestDisplayLayoutManager layout_manager(displays.Pass(), + MULTIPLE_DISPLAY_STATE_SINGLE); + + DisplayConfigurator::ContentProtections request; + request[1] = CONTENT_PROTECTION_METHOD_HDCP; + ApplyContentProtectionTask task( + &layout_manager, &display_delegate_, request, + base::Bind(&ApplyContentProtectionTaskTest::ResponseCallback, + base::Unretained(this))); + task.Run(); + + EXPECT_EQ(ERROR, response_); + EXPECT_EQ(kNoActions, log_.GetActionsAndClear()); +} + +TEST_F(ApplyContentProtectionTaskTest, FailGettingHDCPState) { + ScopedVector<DisplaySnapshot> displays; + displays.push_back(CreateDisplaySnapshot(1, DISPLAY_CONNECTION_TYPE_HDMI)); + TestDisplayLayoutManager layout_manager(displays.Pass(), + MULTIPLE_DISPLAY_STATE_SINGLE); + display_delegate_.set_get_hdcp_state_expectation(false); + + DisplayConfigurator::ContentProtections request; + request[1] = CONTENT_PROTECTION_METHOD_HDCP; + ApplyContentProtectionTask task( + &layout_manager, &display_delegate_, request, + base::Bind(&ApplyContentProtectionTaskTest::ResponseCallback, + base::Unretained(this))); + task.Run(); + + EXPECT_EQ(ERROR, response_); + EXPECT_EQ(kNoActions, log_.GetActionsAndClear()); +} + +TEST_F(ApplyContentProtectionTaskTest, FailSettingHDCPState) { + ScopedVector<DisplaySnapshot> displays; + displays.push_back(CreateDisplaySnapshot(1, DISPLAY_CONNECTION_TYPE_HDMI)); + TestDisplayLayoutManager layout_manager(displays.Pass(), + MULTIPLE_DISPLAY_STATE_SINGLE); + display_delegate_.set_set_hdcp_state_expectation(false); + + DisplayConfigurator::ContentProtections request; + request[1] = CONTENT_PROTECTION_METHOD_HDCP; + ApplyContentProtectionTask task( + &layout_manager, &display_delegate_, request, + base::Bind(&ApplyContentProtectionTaskTest::ResponseCallback, + base::Unretained(this))); + task.Run(); + + EXPECT_EQ(ERROR, response_); + EXPECT_EQ( + JoinActions(GetSetHDCPStateAction(*layout_manager.GetDisplayStates()[0], + HDCP_STATE_DESIRED).c_str(), + NULL), + log_.GetActionsAndClear()); +} + +TEST_F(ApplyContentProtectionTaskTest, ApplyNoopProtection) { + ScopedVector<DisplaySnapshot> displays; + displays.push_back(CreateDisplaySnapshot(1, DISPLAY_CONNECTION_TYPE_HDMI)); + TestDisplayLayoutManager layout_manager(displays.Pass(), + MULTIPLE_DISPLAY_STATE_SINGLE); + display_delegate_.set_hdcp_state(HDCP_STATE_UNDESIRED); + + DisplayConfigurator::ContentProtections request; + request[1] = CONTENT_PROTECTION_METHOD_NONE; + ApplyContentProtectionTask task( + &layout_manager, &display_delegate_, request, + base::Bind(&ApplyContentProtectionTaskTest::ResponseCallback, + base::Unretained(this))); + task.Run(); + + EXPECT_EQ(SUCCESS, response_); + EXPECT_EQ(kNoActions, log_.GetActionsAndClear()); +} + +} // namespace test +} // namespace ui
diff --git a/ui/display/chromeos/display_configurator.cc b/ui/display/chromeos/display_configurator.cc index 8b1f1f7c2..48e31ab 100644 --- a/ui/display/chromeos/display_configurator.cc +++ b/ui/display/chromeos/display_configurator.cc
@@ -9,6 +9,7 @@ #include "base/logging.h" #include "base/sys_info.h" #include "base/time/time.h" +#include "ui/display/chromeos/display_layout_manager.h" #include "ui/display/chromeos/display_util.h" #include "ui/display/chromeos/update_display_configuration_task.h" #include "ui/display/display_switches.h" @@ -81,6 +82,8 @@ chromeos::DisplayPowerState new_power_state, std::vector<DisplayConfigureRequest>* requests, gfx::Size* framebuffer_size) const override; + DisplayStateList GetDisplayStates() const override; + bool IsMirroring() const override; private: // Parses the |displays| into a list of DisplayStates. This effectively adds @@ -329,6 +332,19 @@ return true; } +DisplayConfigurator::DisplayStateList +DisplayConfigurator::DisplayLayoutManagerImpl::GetDisplayStates() const { + return configurator_->cached_displays(); +} + +bool DisplayConfigurator::DisplayLayoutManagerImpl::IsMirroring() const { + if (GetDisplayState() == MULTIPLE_DISPLAY_STATE_DUAL_MIRROR) + return true; + + return GetSoftwareMirroringController() && + GetSoftwareMirroringController()->SoftwareMirroringEnabled(); +} + const DisplayMode* DisplayConfigurator::DisplayLayoutManagerImpl::GetUserSelectedMode( const DisplaySnapshot& display) const { @@ -622,33 +638,39 @@ ApplyProtections(protections); } -bool DisplayConfigurator::QueryContentProtectionStatus( +void DisplayConfigurator::QueryContentProtectionStatus( ContentProtectionClientId client_id, int64_t display_id, - uint32_t* link_mask, - uint32_t* protection_mask) { - if (!configure_display_ || display_externally_controlled_) - return false; + const QueryProtectionCallback& callback) { + QueryProtectionResponse response; + if (!configure_display_ || display_externally_controlled_) { + callback.Run(response); + return; + } uint32_t enabled = 0; uint32_t unfulfilled = 0; - *link_mask = 0; + uint32_t link_mask = 0; for (const DisplaySnapshot* display : cached_displays_) { // Query display if it is in mirror mode or client on the same display. if (!IsMirroring() && display->display_id() != display_id) continue; - *link_mask |= display->type(); + link_mask |= display->type(); switch (display->type()) { case DISPLAY_CONNECTION_TYPE_UNKNOWN: - return false; + callback.Run(response); + return; // DisplayPort, DVI, and HDMI all support HDCP. case DISPLAY_CONNECTION_TYPE_DISPLAYPORT: case DISPLAY_CONNECTION_TYPE_DVI: case DISPLAY_CONNECTION_TYPE_HDMI: { HDCPState state; - if (!native_display_delegate_->GetHDCPState(*display, &state)) - return false; + if (!native_display_delegate_->GetHDCPState(*display, &state)) { + callback.Run(response); + return; + } + if (state == HDCP_STATE_ENABLED) enabled |= CONTENT_PROTECTION_METHOD_HDCP; else @@ -672,19 +694,23 @@ uint32_t requested_mask = 0; if (it->second.find(display_id) != it->second.end()) requested_mask = it->second[display_id]; - *protection_mask = enabled & ~unfulfilled & requested_mask; - } else { - *protection_mask = 0; + response.protection_mask = enabled & ~unfulfilled & requested_mask; } - return true; + + response.success = true; + response.link_mask = link_mask; + callback.Run(response); } -bool DisplayConfigurator::EnableContentProtection( +void DisplayConfigurator::EnableContentProtection( ContentProtectionClientId client_id, int64_t display_id, - uint32_t desired_method_mask) { - if (!configure_display_ || display_externally_controlled_) - return false; + uint32_t desired_method_mask, + const EnableProtectionCallback& callback) { + if (!configure_display_ || display_externally_controlled_) { + callback.Run(false); + return; + } ContentProtections protections; for (const auto& requests_pair : client_protection_requests_) { @@ -698,8 +724,10 @@ } protections[display_id] |= desired_method_mask; - if (!ApplyProtections(protections)) - return false; + if (!ApplyProtections(protections)) { + callback.Run(false); + return; + } if (desired_method_mask == CONTENT_PROTECTION_METHOD_NONE) { if (client_protection_requests_.find(client_id) != @@ -712,7 +740,7 @@ client_protection_requests_[client_id][display_id] = desired_method_mask; } - return true; + callback.Run(true); } std::vector<ui::ColorCalibrationProfile>
diff --git a/ui/display/chromeos/display_configurator.h b/ui/display/chromeos/display_configurator.h index 88eab83..7c83dd2 100644 --- a/ui/display/chromeos/display_configurator.h +++ b/ui/display/chromeos/display_configurator.h
@@ -28,7 +28,7 @@ } namespace ui { -struct DisplayConfigureRequest; +class DisplayLayoutManager; class DisplayMode; class DisplaySnapshot; class NativeDisplayDelegate; @@ -42,8 +42,29 @@ typedef base::Callback<void(bool)> ConfigurationCallback; + typedef base::Callback<void(bool /* success */)> EnableProtectionCallback; + + struct QueryProtectionResponse { + // True if the query succeeded, false otherwise. + bool success = false; + + // The type of connected display links, which is a bitmask of + // DisplayConnectionType values. + uint32_t link_mask = 0; + + // The desired protection methods, which is a bitmask of the + // ContentProtectionMethod values. + uint32_t protection_mask = 0; + }; + + typedef base::Callback<void(const QueryProtectionResponse&)> + QueryProtectionCallback; + typedef std::vector<DisplaySnapshot*> DisplayStateList; + // Mapping a display_id to a protection request bitmask. + typedef std::map<int64_t, uint32_t> ContentProtections; + class Observer { public: virtual ~Observer() {} @@ -89,33 +110,6 @@ virtual bool SoftwareMirroringEnabled() const = 0; }; - class DisplayLayoutManager { - public: - virtual ~DisplayLayoutManager() {} - - virtual SoftwareMirroringController* GetSoftwareMirroringController() - const = 0; - - virtual StateController* GetStateController() const = 0; - - // Returns the current display state. - virtual MultipleDisplayState GetDisplayState() const = 0; - - // Returns the current power state. - virtual chromeos::DisplayPowerState GetPowerState() const = 0; - - // Based on the given |displays|, display state and power state, it will - // create display configuration requests which will then be used to - // configure the hardware. The requested configuration is stored in - // |requests| and |framebuffer_size|. - virtual bool GetDisplayLayout( - const std::vector<DisplaySnapshot*>& displays, - MultipleDisplayState new_display_state, - chromeos::DisplayPowerState new_power_state, - std::vector<DisplayConfigureRequest>* requests, - gfx::Size* framebuffer_size) const = 0; - }; - // Helper class used by tests. class TestApi { public: @@ -240,23 +234,20 @@ // Unregisters the client. void UnregisterContentProtectionClient(ContentProtectionClientId client_id); - // Queries link status and protection status. - // |link_mask| is the type of connected display links, which is a bitmask of - // DisplayConnectionType values. |protection_mask| is the desired protection - // methods, which is a bitmask of the ContentProtectionMethod values. - // Returns true on success. - bool QueryContentProtectionStatus(ContentProtectionClientId client_id, + // Queries link status and protection status. |callback| is used to respond + // to the query. + void QueryContentProtectionStatus(ContentProtectionClientId client_id, int64_t display_id, - uint32_t* link_mask, - uint32_t* protection_mask); + const QueryProtectionCallback& callback); // Requests the desired protection methods. // |protection_mask| is the desired protection methods, which is a bitmask // of the ContentProtectionMethod values. // Returns true when the protection request has been made. - bool EnableContentProtection(ContentProtectionClientId client_id, + void EnableContentProtection(ContentProtectionClientId client_id, int64_t display_id, - uint32_t desired_protection_mask); + uint32_t protection_mask, + const EnableProtectionCallback& callback); // Checks the available color profiles for |display_id| and fills the result // into |profiles|. @@ -270,8 +261,6 @@ private: class DisplayLayoutManagerImpl; - // Mapping a display_id to a protection request bitmask. - typedef std::map<int64_t, uint32_t> ContentProtections; // Mapping a client to its protection request. typedef std::map<ContentProtectionClientId, ContentProtections> ProtectionRequests;
diff --git a/ui/display/chromeos/display_configurator_unittest.cc b/ui/display/chromeos/display_configurator_unittest.cc index f819184c..9000af6 100644 --- a/ui/display/chromeos/display_configurator_unittest.cc +++ b/ui/display/chromeos/display_configurator_unittest.cc
@@ -125,6 +125,9 @@ big_mode_(gfx::Size(2560, 1600), false, 60.0f), observer_(&configurator_), test_api_(&configurator_), + enable_content_protection_status_(0), + enable_content_protection_call_count_(0), + query_content_protection_call_count_(0), callback_result_(CALLBACK_NOT_CALLED) {} ~DisplayConfiguratorTest() override {} @@ -165,6 +168,17 @@ callback_result_ = (status ? CALLBACK_SUCCESS : CALLBACK_FAILURE); } + void EnableContentProtectionCallback(bool status) { + enable_content_protection_status_ = status; + enable_content_protection_call_count_++; + } + + void QueryContentProtectionCallback( + const DisplayConfigurator::QueryProtectionResponse& response) { + query_content_protection_response_ = response; + query_content_protection_call_count_++; + } + // Predefined modes that can be used by outputs. const DisplayMode small_mode_; const DisplayMode big_mode_; @@ -219,6 +233,12 @@ TestNativeDisplayDelegate* native_display_delegate_; // not owned DisplayConfigurator::TestApi test_api_; + bool enable_content_protection_status_; + int enable_content_protection_call_count_; + DisplayConfigurator::QueryProtectionResponse + query_content_protection_response_; + int query_content_protection_call_count_; + TestDisplaySnapshot outputs_[2]; CallbackResult callback_result_; @@ -937,37 +957,54 @@ // One output. UpdateOutputs(1, true); EXPECT_NE(kNoActions, log_->GetActionsAndClear()); - uint32_t link_mask = 0; - uint32_t protection_mask = 0; - EXPECT_TRUE(configurator_.QueryContentProtectionStatus( - id, outputs_[0].display_id(), &link_mask, &protection_mask)); - EXPECT_EQ(static_cast<uint32_t>(DISPLAY_CONNECTION_TYPE_INTERNAL), link_mask); + configurator_.QueryContentProtectionStatus( + id, outputs_[0].display_id(), + base::Bind(&DisplayConfiguratorTest::QueryContentProtectionCallback, + base::Unretained(this))); + EXPECT_EQ(1, query_content_protection_call_count_); + EXPECT_TRUE(query_content_protection_response_.success); + EXPECT_EQ(static_cast<uint32_t>(DISPLAY_CONNECTION_TYPE_INTERNAL), + query_content_protection_response_.link_mask); EXPECT_EQ(static_cast<uint32_t>(CONTENT_PROTECTION_METHOD_NONE), - protection_mask); + query_content_protection_response_.protection_mask); EXPECT_EQ(kNoActions, log_->GetActionsAndClear()); // Two outputs. UpdateOutputs(2, true); EXPECT_NE(kNoActions, log_->GetActionsAndClear()); - EXPECT_TRUE(configurator_.QueryContentProtectionStatus( - id, outputs_[1].display_id(), &link_mask, &protection_mask)); - EXPECT_EQ(static_cast<uint32_t>(DISPLAY_CONNECTION_TYPE_HDMI), link_mask); + configurator_.QueryContentProtectionStatus( + id, outputs_[1].display_id(), + base::Bind(&DisplayConfiguratorTest::QueryContentProtectionCallback, + base::Unretained(this))); + EXPECT_EQ(2, query_content_protection_call_count_); + EXPECT_TRUE(query_content_protection_response_.success); + EXPECT_EQ(static_cast<uint32_t>(DISPLAY_CONNECTION_TYPE_HDMI), + query_content_protection_response_.link_mask); EXPECT_EQ(static_cast<uint32_t>(CONTENT_PROTECTION_METHOD_NONE), - protection_mask); + query_content_protection_response_.protection_mask); EXPECT_EQ(kNoActions, log_->GetActionsAndClear()); - EXPECT_TRUE(configurator_.EnableContentProtection( - id, outputs_[1].display_id(), CONTENT_PROTECTION_METHOD_HDCP)); + configurator_.EnableContentProtection( + id, outputs_[1].display_id(), CONTENT_PROTECTION_METHOD_HDCP, + base::Bind(&DisplayConfiguratorTest::EnableContentProtectionCallback, + base::Unretained(this))); + EXPECT_EQ(1, enable_content_protection_call_count_); + EXPECT_TRUE(enable_content_protection_status_); EXPECT_EQ(GetSetHDCPStateAction(outputs_[1], HDCP_STATE_DESIRED), log_->GetActionsAndClear()); // Enable protection. native_display_delegate_->set_hdcp_state(HDCP_STATE_ENABLED); - EXPECT_TRUE(configurator_.QueryContentProtectionStatus( - id, outputs_[1].display_id(), &link_mask, &protection_mask)); - EXPECT_EQ(static_cast<uint32_t>(DISPLAY_CONNECTION_TYPE_HDMI), link_mask); + configurator_.QueryContentProtectionStatus( + id, outputs_[1].display_id(), + base::Bind(&DisplayConfiguratorTest::QueryContentProtectionCallback, + base::Unretained(this))); + EXPECT_EQ(3, query_content_protection_call_count_); + EXPECT_TRUE(query_content_protection_response_.success); + EXPECT_EQ(static_cast<uint32_t>(DISPLAY_CONNECTION_TYPE_HDMI), + query_content_protection_response_.link_mask); EXPECT_EQ(static_cast<uint32_t>(CONTENT_PROTECTION_METHOD_HDCP), - protection_mask); + query_content_protection_response_.protection_mask); EXPECT_EQ(kNoActions, log_->GetActionsAndClear()); // Protections should be disabled after unregister. @@ -1086,30 +1123,53 @@ EXPECT_NE(kNoActions, log_->GetActionsAndClear()); // Clients never know state enableness for methods that they didn't request. - EXPECT_TRUE(configurator_.EnableContentProtection( - client1, outputs_[1].display_id(), CONTENT_PROTECTION_METHOD_HDCP)); + configurator_.EnableContentProtection( + client1, outputs_[1].display_id(), CONTENT_PROTECTION_METHOD_HDCP, + base::Bind(&DisplayConfiguratorTest::EnableContentProtectionCallback, + base::Unretained(this))); + EXPECT_EQ(1, enable_content_protection_call_count_); + EXPECT_TRUE(enable_content_protection_status_); EXPECT_EQ(GetSetHDCPStateAction(outputs_[1], HDCP_STATE_DESIRED).c_str(), log_->GetActionsAndClear()); native_display_delegate_->set_hdcp_state(HDCP_STATE_ENABLED); - uint32_t link_mask = 0; - uint32_t protection_mask = 0; - EXPECT_TRUE(configurator_.QueryContentProtectionStatus( - client1, outputs_[1].display_id(), &link_mask, &protection_mask)); - EXPECT_EQ(static_cast<uint32_t>(DISPLAY_CONNECTION_TYPE_HDMI), link_mask); - EXPECT_EQ(CONTENT_PROTECTION_METHOD_HDCP, protection_mask); + configurator_.QueryContentProtectionStatus( + client1, outputs_[1].display_id(), + base::Bind(&DisplayConfiguratorTest::QueryContentProtectionCallback, + base::Unretained(this))); + EXPECT_EQ(1, query_content_protection_call_count_); + EXPECT_TRUE(query_content_protection_response_.success); + EXPECT_EQ(static_cast<uint32_t>(DISPLAY_CONNECTION_TYPE_HDMI), + query_content_protection_response_.link_mask); + EXPECT_EQ(CONTENT_PROTECTION_METHOD_HDCP, + query_content_protection_response_.protection_mask); - EXPECT_TRUE(configurator_.QueryContentProtectionStatus( - client2, outputs_[1].display_id(), &link_mask, &protection_mask)); - EXPECT_EQ(static_cast<uint32_t>(DISPLAY_CONNECTION_TYPE_HDMI), link_mask); - EXPECT_EQ(CONTENT_PROTECTION_METHOD_NONE, protection_mask); + configurator_.QueryContentProtectionStatus( + client2, outputs_[1].display_id(), + base::Bind(&DisplayConfiguratorTest::QueryContentProtectionCallback, + base::Unretained(this))); + EXPECT_EQ(2, query_content_protection_call_count_); + EXPECT_TRUE(query_content_protection_response_.success); + EXPECT_EQ(static_cast<uint32_t>(DISPLAY_CONNECTION_TYPE_HDMI), + query_content_protection_response_.link_mask); + EXPECT_EQ(CONTENT_PROTECTION_METHOD_NONE, + query_content_protection_response_.protection_mask); // Protections will be disabled only if no more clients request them. - EXPECT_TRUE(configurator_.EnableContentProtection( - client2, outputs_[1].display_id(), CONTENT_PROTECTION_METHOD_NONE)); + configurator_.EnableContentProtection( + client2, outputs_[1].display_id(), CONTENT_PROTECTION_METHOD_NONE, + base::Bind(&DisplayConfiguratorTest::EnableContentProtectionCallback, + base::Unretained(this))); + EXPECT_EQ(2, enable_content_protection_call_count_); + EXPECT_TRUE(enable_content_protection_status_); EXPECT_EQ(kNoActions, log_->GetActionsAndClear()); - EXPECT_TRUE(configurator_.EnableContentProtection( - client1, outputs_[1].display_id(), CONTENT_PROTECTION_METHOD_NONE)); + + configurator_.EnableContentProtection( + client1, outputs_[1].display_id(), CONTENT_PROTECTION_METHOD_NONE, + base::Bind(&DisplayConfiguratorTest::EnableContentProtectionCallback, + base::Unretained(this))); + EXPECT_EQ(3, enable_content_protection_call_count_); + EXPECT_TRUE(enable_content_protection_status_); EXPECT_EQ(GetSetHDCPStateAction(outputs_[1], HDCP_STATE_UNDESIRED).c_str(), log_->GetActionsAndClear()); } @@ -1127,20 +1187,36 @@ log_->GetActionsAndClear(); // Only enable once if HDCP is enabling. - EXPECT_TRUE(configurator_.EnableContentProtection( - client1, outputs_[1].display_id(), CONTENT_PROTECTION_METHOD_HDCP)); + configurator_.EnableContentProtection( + client1, outputs_[1].display_id(), CONTENT_PROTECTION_METHOD_HDCP, + base::Bind(&DisplayConfiguratorTest::EnableContentProtectionCallback, + base::Unretained(this))); + EXPECT_EQ(1, enable_content_protection_call_count_); + EXPECT_TRUE(enable_content_protection_status_); native_display_delegate_->set_hdcp_state(HDCP_STATE_DESIRED); - EXPECT_TRUE(configurator_.EnableContentProtection( - client2, outputs_[1].display_id(), CONTENT_PROTECTION_METHOD_HDCP)); + configurator_.EnableContentProtection( + client2, outputs_[1].display_id(), CONTENT_PROTECTION_METHOD_HDCP, + base::Bind(&DisplayConfiguratorTest::EnableContentProtectionCallback, + base::Unretained(this))); + EXPECT_EQ(2, enable_content_protection_call_count_); + EXPECT_TRUE(enable_content_protection_status_); EXPECT_EQ(GetSetHDCPStateAction(outputs_[1], HDCP_STATE_DESIRED).c_str(), log_->GetActionsAndClear()); native_display_delegate_->set_hdcp_state(HDCP_STATE_ENABLED); // Don't enable again if HDCP is already active. - EXPECT_TRUE(configurator_.EnableContentProtection( - client1, outputs_[1].display_id(), CONTENT_PROTECTION_METHOD_HDCP)); - EXPECT_TRUE(configurator_.EnableContentProtection( - client2, outputs_[1].display_id(), CONTENT_PROTECTION_METHOD_HDCP)); + configurator_.EnableContentProtection( + client1, outputs_[1].display_id(), CONTENT_PROTECTION_METHOD_HDCP, + base::Bind(&DisplayConfiguratorTest::EnableContentProtectionCallback, + base::Unretained(this))); + EXPECT_EQ(3, enable_content_protection_call_count_); + EXPECT_TRUE(enable_content_protection_status_); + configurator_.EnableContentProtection( + client2, outputs_[1].display_id(), CONTENT_PROTECTION_METHOD_HDCP, + base::Bind(&DisplayConfiguratorTest::EnableContentProtectionCallback, + base::Unretained(this))); + EXPECT_EQ(4, enable_content_protection_call_count_); + EXPECT_TRUE(enable_content_protection_status_); EXPECT_EQ(kNoActions, log_->GetActionsAndClear()); }
diff --git a/ui/display/chromeos/display_layout_manager.h b/ui/display/chromeos/display_layout_manager.h new file mode 100644 index 0000000..068f118 --- /dev/null +++ b/ui/display/chromeos/display_layout_manager.h
@@ -0,0 +1,51 @@ +// 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_DISPLAY_CHROMEOS_DISPLAY_LAYOUT_MANAGER_H_ +#define UI_DISPLAY_CHROMEOS_DISPLAY_LAYOUT_MANAGER_H_ + +#include <vector> + +#include "third_party/cros_system_api/dbus/service_constants.h" +#include "ui/display/chromeos/display_configurator.h" +#include "ui/display/types/display_constants.h" + +namespace ui { + +struct DisplayConfigureRequest; +class DisplaySnapshot; + +class DisplayLayoutManager { + public: + virtual ~DisplayLayoutManager() {} + + virtual DisplayConfigurator::SoftwareMirroringController* + GetSoftwareMirroringController() const = 0; + + virtual DisplayConfigurator::StateController* GetStateController() const = 0; + + // Returns the current display state. + virtual MultipleDisplayState GetDisplayState() const = 0; + + // Returns the current power state. + virtual chromeos::DisplayPowerState GetPowerState() const = 0; + + // Based on the given |displays|, display state and power state, it will + // create display configuration requests which will then be used to + // configure the hardware. The requested configuration is stored in + // |requests| and |framebuffer_size|. + virtual bool GetDisplayLayout(const std::vector<DisplaySnapshot*>& displays, + MultipleDisplayState new_display_state, + chromeos::DisplayPowerState new_power_state, + std::vector<DisplayConfigureRequest>* requests, + gfx::Size* framebuffer_size) const = 0; + + virtual std::vector<DisplaySnapshot*> GetDisplayStates() const = 0; + + virtual bool IsMirroring() const = 0; +}; + +} // namespace ui + +#endif // UI_DISPLAY_CHROMEOS_DISPLAY_LAYOUT_MANAGER_H_
diff --git a/ui/display/chromeos/query_content_protection_task.cc b/ui/display/chromeos/query_content_protection_task.cc new file mode 100644 index 0000000..d90d55f --- /dev/null +++ b/ui/display/chromeos/query_content_protection_task.cc
@@ -0,0 +1,87 @@ +// 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/display/chromeos/query_content_protection_task.h" + +#include "ui/display/chromeos/display_layout_manager.h" +#include "ui/display/types/display_snapshot.h" +#include "ui/display/types/native_display_delegate.h" + +namespace ui { + +QueryContentProtectionTask::QueryContentProtectionTask( + DisplayLayoutManager* layout_manager, + NativeDisplayDelegate* native_display_delegate, + int64_t display_id, + const ResponseCallback& callback) + : layout_manager_(layout_manager), + native_display_delegate_(native_display_delegate), + display_id_(display_id), + callback_(callback), + pending_requests_(0), + weak_ptr_factory_(this) { +} + +QueryContentProtectionTask::~QueryContentProtectionTask() { +} + +void QueryContentProtectionTask::Run() { + std::vector<DisplaySnapshot*> hdcp_capable_displays; + for (DisplaySnapshot* display : layout_manager_->GetDisplayStates()) { + // Query display if it is in mirror mode or client on the same display. + if (!layout_manager_->IsMirroring() && display->display_id() != display_id_) + continue; + + response_.link_mask |= display->type(); + + switch (display->type()) { + case DISPLAY_CONNECTION_TYPE_UNKNOWN: + callback_.Run(response_); + return; + case DISPLAY_CONNECTION_TYPE_DISPLAYPORT: + case DISPLAY_CONNECTION_TYPE_DVI: + case DISPLAY_CONNECTION_TYPE_HDMI: + hdcp_capable_displays.push_back(display); + break; + case DISPLAY_CONNECTION_TYPE_INTERNAL: + case DISPLAY_CONNECTION_TYPE_VGA: + case DISPLAY_CONNECTION_TYPE_NETWORK: + // No protections for these types. Do nothing. + break; + case DISPLAY_CONNECTION_TYPE_NONE: + NOTREACHED(); + break; + } + } + + response_.success = true; + pending_requests_ = hdcp_capable_displays.size(); + if (pending_requests_ != 0) { + for (DisplaySnapshot* display : hdcp_capable_displays) { + native_display_delegate_->GetHDCPState( + *display, base::Bind(&QueryContentProtectionTask::OnHDCPStateUpdate, + weak_ptr_factory_.GetWeakPtr())); + } + } else { + callback_.Run(response_); + } +} + +void QueryContentProtectionTask::OnHDCPStateUpdate(bool success, + HDCPState state) { + response_.success &= success; + if (state == HDCP_STATE_ENABLED) + response_.enabled |= CONTENT_PROTECTION_METHOD_HDCP; + else + response_.unfulfilled |= CONTENT_PROTECTION_METHOD_HDCP; + + pending_requests_--; + // Wait for all the requests to finish before invoking the callback. + if (pending_requests_ != 0) + return; + + callback_.Run(response_); +} + +} // namespace ui
diff --git a/ui/display/chromeos/query_content_protection_task.h b/ui/display/chromeos/query_content_protection_task.h new file mode 100644 index 0000000..863b286 --- /dev/null +++ b/ui/display/chromeos/query_content_protection_task.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_DISPLAY_CHROMEOS_QUERY_CONTENT_PROTECTION_TASK_H_ +#define UI_DISPLAY_CHROMEOS_QUERY_CONTENT_PROTECTION_TASK_H_ + +#include "base/callback.h" +#include "base/memory/weak_ptr.h" +#include "ui/display/display_export.h" +#include "ui/display/types/display_constants.h" + +namespace ui { + +class DisplayLayoutManager; +class NativeDisplayDelegate; + +class DISPLAY_EXPORT QueryContentProtectionTask { + public: + struct Response { + bool success = false; + uint32_t link_mask = 0; + uint32_t enabled = 0; + uint32_t unfulfilled = 0; + }; + + typedef base::Callback<void(Response)> ResponseCallback; + + QueryContentProtectionTask(DisplayLayoutManager* layout_manager, + NativeDisplayDelegate* native_display_delegate, + int64_t display_id, + const ResponseCallback& callback); + ~QueryContentProtectionTask(); + + void Run(); + + private: + // Callback for NativeDisplayDelegate::GetHDCPState() + void OnHDCPStateUpdate(bool success, HDCPState state); + + DisplayLayoutManager* layout_manager_; // Not owned. + + NativeDisplayDelegate* native_display_delegate_; // Not owned. + + // Display ID for the query. + int64_t display_id_; + + // Called at the end of the query to signal completion. + ResponseCallback callback_; + + Response response_; + + // Tracks the number of NativeDisplayDelegate requests sent but not answered + // yet. + size_t pending_requests_; + + base::WeakPtrFactory<QueryContentProtectionTask> weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(QueryContentProtectionTask); +}; + +} // namespace ui + +#endif // UI_DISPLAY_CHROMEOS_QUERY_CONTENT_PROTECTION_TASK_H_
diff --git a/ui/display/chromeos/query_content_protection_task_unittest.cc b/ui/display/chromeos/query_content_protection_task_unittest.cc new file mode 100644 index 0000000..2cd2cb2 --- /dev/null +++ b/ui/display/chromeos/query_content_protection_task_unittest.cc
@@ -0,0 +1,191 @@ +// 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/bind.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/display/chromeos/display_layout_manager.h" +#include "ui/display/chromeos/query_content_protection_task.h" +#include "ui/display/chromeos/test/action_logger_util.h" +#include "ui/display/chromeos/test/test_display_layout_manager.h" +#include "ui/display/chromeos/test/test_display_snapshot.h" +#include "ui/display/chromeos/test/test_native_display_delegate.h" + +namespace ui { +namespace test { + +namespace { + +scoped_ptr<DisplaySnapshot> CreateDisplaySnapshot(int64_t id, + DisplayConnectionType type) { + scoped_ptr<TestDisplaySnapshot> display(new TestDisplaySnapshot()); + display->set_display_id(id); + display->set_type(type); + + return display.Pass(); +} + +} // namespace + +class QueryContentProtectionTaskTest : public testing::Test { + public: + QueryContentProtectionTaskTest() + : display_delegate_(&log_), has_response_(false) {} + ~QueryContentProtectionTaskTest() override {} + + void ResponseCallback(QueryContentProtectionTask::Response response) { + has_response_ = true; + response_ = response; + } + + protected: + ActionLogger log_; + TestNativeDisplayDelegate display_delegate_; + + bool has_response_; + QueryContentProtectionTask::Response response_; + + private: + DISALLOW_COPY_AND_ASSIGN(QueryContentProtectionTaskTest); +}; + +TEST_F(QueryContentProtectionTaskTest, QueryWithNoHDCPCapableDisplay) { + ScopedVector<DisplaySnapshot> displays; + displays.push_back( + CreateDisplaySnapshot(1, DISPLAY_CONNECTION_TYPE_INTERNAL)); + TestDisplayLayoutManager layout_manager(displays.Pass(), + MULTIPLE_DISPLAY_STATE_SINGLE); + + QueryContentProtectionTask task( + &layout_manager, &display_delegate_, 1, + base::Bind(&QueryContentProtectionTaskTest::ResponseCallback, + base::Unretained(this))); + task.Run(); + + EXPECT_TRUE(has_response_); + EXPECT_TRUE(response_.success); + EXPECT_EQ(DISPLAY_CONNECTION_TYPE_INTERNAL, response_.link_mask); + EXPECT_EQ(0u, response_.enabled); + EXPECT_EQ(0u, response_.unfulfilled); +} + +TEST_F(QueryContentProtectionTaskTest, QueryWithUnknownDisplay) { + ScopedVector<DisplaySnapshot> displays; + displays.push_back(CreateDisplaySnapshot(1, DISPLAY_CONNECTION_TYPE_UNKNOWN)); + TestDisplayLayoutManager layout_manager(displays.Pass(), + MULTIPLE_DISPLAY_STATE_SINGLE); + + QueryContentProtectionTask task( + &layout_manager, &display_delegate_, 1, + base::Bind(&QueryContentProtectionTaskTest::ResponseCallback, + base::Unretained(this))); + task.Run(); + + EXPECT_TRUE(has_response_); + EXPECT_FALSE(response_.success); + EXPECT_EQ(DISPLAY_CONNECTION_TYPE_UNKNOWN, response_.link_mask); + EXPECT_EQ(0u, response_.enabled); + EXPECT_EQ(0u, response_.unfulfilled); +} + +TEST_F(QueryContentProtectionTaskTest, FailQueryWithHDMIDisplay) { + ScopedVector<DisplaySnapshot> displays; + displays.push_back(CreateDisplaySnapshot(1, DISPLAY_CONNECTION_TYPE_HDMI)); + TestDisplayLayoutManager layout_manager(displays.Pass(), + MULTIPLE_DISPLAY_STATE_SINGLE); + display_delegate_.set_get_hdcp_state_expectation(false); + + QueryContentProtectionTask task( + &layout_manager, &display_delegate_, 1, + base::Bind(&QueryContentProtectionTaskTest::ResponseCallback, + base::Unretained(this))); + task.Run(); + + EXPECT_TRUE(has_response_); + EXPECT_FALSE(response_.success); + EXPECT_EQ(DISPLAY_CONNECTION_TYPE_HDMI, response_.link_mask); +} + +TEST_F(QueryContentProtectionTaskTest, QueryWithHDMIDisplayAndUnfulfilled) { + ScopedVector<DisplaySnapshot> displays; + displays.push_back(CreateDisplaySnapshot(1, DISPLAY_CONNECTION_TYPE_HDMI)); + TestDisplayLayoutManager layout_manager(displays.Pass(), + MULTIPLE_DISPLAY_STATE_SINGLE); + + QueryContentProtectionTask task( + &layout_manager, &display_delegate_, 1, + base::Bind(&QueryContentProtectionTaskTest::ResponseCallback, + base::Unretained(this))); + task.Run(); + + EXPECT_TRUE(has_response_); + EXPECT_TRUE(response_.success); + EXPECT_EQ(DISPLAY_CONNECTION_TYPE_HDMI, response_.link_mask); + EXPECT_EQ(0u, response_.enabled); + EXPECT_EQ(CONTENT_PROTECTION_METHOD_HDCP, response_.unfulfilled); +} + +TEST_F(QueryContentProtectionTaskTest, QueryWithHDMIDisplayAndFulfilled) { + ScopedVector<DisplaySnapshot> displays; + displays.push_back(CreateDisplaySnapshot(1, DISPLAY_CONNECTION_TYPE_HDMI)); + TestDisplayLayoutManager layout_manager(displays.Pass(), + MULTIPLE_DISPLAY_STATE_SINGLE); + display_delegate_.set_hdcp_state(HDCP_STATE_ENABLED); + + QueryContentProtectionTask task( + &layout_manager, &display_delegate_, 1, + base::Bind(&QueryContentProtectionTaskTest::ResponseCallback, + base::Unretained(this))); + task.Run(); + + EXPECT_TRUE(has_response_); + EXPECT_TRUE(response_.success); + EXPECT_EQ(DISPLAY_CONNECTION_TYPE_HDMI, response_.link_mask); + EXPECT_EQ(CONTENT_PROTECTION_METHOD_HDCP, response_.enabled); + EXPECT_EQ(0u, response_.unfulfilled); +} + +TEST_F(QueryContentProtectionTaskTest, QueryWith2HDCPDisplays) { + ScopedVector<DisplaySnapshot> displays; + displays.push_back(CreateDisplaySnapshot(1, DISPLAY_CONNECTION_TYPE_HDMI)); + displays.push_back(CreateDisplaySnapshot(2, DISPLAY_CONNECTION_TYPE_DVI)); + TestDisplayLayoutManager layout_manager(displays.Pass(), + MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED); + + QueryContentProtectionTask task( + &layout_manager, &display_delegate_, 1, + base::Bind(&QueryContentProtectionTaskTest::ResponseCallback, + base::Unretained(this))); + task.Run(); + + EXPECT_TRUE(has_response_); + EXPECT_TRUE(response_.success); + EXPECT_EQ(DISPLAY_CONNECTION_TYPE_HDMI, response_.link_mask); + EXPECT_EQ(0u, response_.enabled); + EXPECT_EQ(CONTENT_PROTECTION_METHOD_HDCP, response_.unfulfilled); +} + +TEST_F(QueryContentProtectionTaskTest, QueryWithMirrorHDCPDisplays) { + ScopedVector<DisplaySnapshot> displays; + displays.push_back(CreateDisplaySnapshot(1, DISPLAY_CONNECTION_TYPE_HDMI)); + displays.push_back(CreateDisplaySnapshot(2, DISPLAY_CONNECTION_TYPE_DVI)); + TestDisplayLayoutManager layout_manager(displays.Pass(), + MULTIPLE_DISPLAY_STATE_DUAL_MIRROR); + + QueryContentProtectionTask task( + &layout_manager, &display_delegate_, 1, + base::Bind(&QueryContentProtectionTaskTest::ResponseCallback, + base::Unretained(this))); + task.Run(); + + EXPECT_TRUE(has_response_); + EXPECT_TRUE(response_.success); + EXPECT_EQ(static_cast<uint32_t>(DISPLAY_CONNECTION_TYPE_HDMI | + DISPLAY_CONNECTION_TYPE_DVI), + response_.link_mask); + EXPECT_EQ(0u, response_.enabled); + EXPECT_EQ(CONTENT_PROTECTION_METHOD_HDCP, response_.unfulfilled); +} + +} // namespace test +} // namespace ui
diff --git a/ui/display/chromeos/test/test_display_layout_manager.cc b/ui/display/chromeos/test/test_display_layout_manager.cc new file mode 100644 index 0000000..2494c9b --- /dev/null +++ b/ui/display/chromeos/test/test_display_layout_manager.cc
@@ -0,0 +1,60 @@ +// 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/display/chromeos/test/test_display_layout_manager.h" + +#include "ui/display/types/display_snapshot.h" + +namespace ui { +namespace test { + +TestDisplayLayoutManager::TestDisplayLayoutManager( + ScopedVector<DisplaySnapshot> displays, + MultipleDisplayState display_state) + : displays_(displays.Pass()), display_state_(display_state) { +} + +TestDisplayLayoutManager::~TestDisplayLayoutManager() { +} + +DisplayConfigurator::StateController* +TestDisplayLayoutManager::GetStateController() const { + return nullptr; +} + +DisplayConfigurator::SoftwareMirroringController* +TestDisplayLayoutManager::GetSoftwareMirroringController() const { + return nullptr; +} + +MultipleDisplayState TestDisplayLayoutManager::GetDisplayState() const { + return display_state_; +} + +chromeos::DisplayPowerState TestDisplayLayoutManager::GetPowerState() const { + NOTREACHED(); + return chromeos::DISPLAY_POWER_ALL_ON; +} + +bool TestDisplayLayoutManager::GetDisplayLayout( + const std::vector<DisplaySnapshot*>& displays, + MultipleDisplayState new_display_state, + chromeos::DisplayPowerState new_power_state, + std::vector<DisplayConfigureRequest>* requests, + gfx::Size* framebuffer_size) const { + NOTREACHED(); + return false; +} + +std::vector<DisplaySnapshot*> TestDisplayLayoutManager::GetDisplayStates() + const { + return displays_.get(); +} + +bool TestDisplayLayoutManager::IsMirroring() const { + return display_state_ == MULTIPLE_DISPLAY_STATE_DUAL_MIRROR; +} + +} // namespace test +} // namespace ui
diff --git a/ui/display/chromeos/test/test_display_layout_manager.h b/ui/display/chromeos/test/test_display_layout_manager.h new file mode 100644 index 0000000..c46f850 --- /dev/null +++ b/ui/display/chromeos/test/test_display_layout_manager.h
@@ -0,0 +1,45 @@ +// 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_DISPLAY_CHROMEOS_TEST_TEST_DISPLAY_LAYOUT_MANAGER_H_ +#define UI_DISPLAY_CHROMEOS_TEST_TEST_DISPLAY_LAYOUT_MANAGER_H_ + +#include "base/memory/scoped_vector.h" +#include "ui/display/chromeos/display_configurator.h" +#include "ui/display/chromeos/display_layout_manager.h" + +namespace ui { +namespace test { + +class TestDisplayLayoutManager : public DisplayLayoutManager { + public: + TestDisplayLayoutManager(ScopedVector<DisplaySnapshot> displays, + MultipleDisplayState display_state); + ~TestDisplayLayoutManager() override; + + // DisplayLayoutManager: + DisplayConfigurator::StateController* GetStateController() const override; + DisplayConfigurator::SoftwareMirroringController* + GetSoftwareMirroringController() const override; + MultipleDisplayState GetDisplayState() const override; + chromeos::DisplayPowerState GetPowerState() const override; + bool GetDisplayLayout(const std::vector<DisplaySnapshot*>& displays, + MultipleDisplayState new_display_state, + chromeos::DisplayPowerState new_power_state, + std::vector<DisplayConfigureRequest>* requests, + gfx::Size* framebuffer_size) const override; + std::vector<DisplaySnapshot*> GetDisplayStates() const override; + bool IsMirroring() const override; + + private: + ScopedVector<DisplaySnapshot> displays_; + MultipleDisplayState display_state_; + + DISALLOW_COPY_AND_ASSIGN(TestDisplayLayoutManager); +}; + +} // namespace test +} // namespace ui + +#endif // UI_DISPLAY_CHROMEOS_TEST_TEST_DISPLAY_LAYOUT_MANAGER_H_
diff --git a/ui/display/chromeos/test/test_native_display_delegate.cc b/ui/display/chromeos/test/test_native_display_delegate.cc index 0da2e11..5533961 100644 --- a/ui/display/chromeos/test/test_native_display_delegate.cc +++ b/ui/display/chromeos/test/test_native_display_delegate.cc
@@ -14,6 +14,8 @@ TestNativeDisplayDelegate::TestNativeDisplayDelegate(ActionLogger* log) : max_configurable_pixels_(0), + get_hdcp_expectation_(true), + set_hdcp_expectation_(true), hdcp_state_(HDCP_STATE_UNDESIRED), run_async_(false), log_(log) { @@ -107,13 +109,27 @@ bool TestNativeDisplayDelegate::GetHDCPState(const DisplaySnapshot& output, HDCPState* state) { *state = hdcp_state_; - return true; + return get_hdcp_expectation_; +} + +void TestNativeDisplayDelegate::GetHDCPState( + const DisplaySnapshot& output, + const GetHDCPStateCallback& callback) { + callback.Run(get_hdcp_expectation_, hdcp_state_); } bool TestNativeDisplayDelegate::SetHDCPState(const DisplaySnapshot& output, HDCPState state) { log_->AppendAction(GetSetHDCPStateAction(output, state)); - return true; + return set_hdcp_expectation_; +} + +void TestNativeDisplayDelegate::SetHDCPState( + const DisplaySnapshot& output, + HDCPState state, + const SetHDCPStateCallback& callback) { + log_->AppendAction(GetSetHDCPStateAction(output, state)); + callback.Run(set_hdcp_expectation_); } std::vector<ui::ColorCalibrationProfile>
diff --git a/ui/display/chromeos/test/test_native_display_delegate.h b/ui/display/chromeos/test/test_native_display_delegate.h index eac789e1..a3ed4c64 100644 --- a/ui/display/chromeos/test/test_native_display_delegate.h +++ b/ui/display/chromeos/test/test_native_display_delegate.h
@@ -35,6 +35,14 @@ max_configurable_pixels_ = pixels; } + void set_get_hdcp_state_expectation(bool success) { + get_hdcp_expectation_ = success; + } + + void set_set_hdcp_state_expectation(bool success) { + set_hdcp_expectation_ = success; + } + void set_hdcp_state(HDCPState state) { hdcp_state_ = state; } void set_run_async(bool run_async) { run_async_ = run_async; } @@ -57,6 +65,11 @@ void CreateFrameBuffer(const gfx::Size& size) override; bool GetHDCPState(const DisplaySnapshot& output, HDCPState* state) override; bool SetHDCPState(const DisplaySnapshot& output, HDCPState state) override; + void GetHDCPState(const DisplaySnapshot& output, + const GetHDCPStateCallback& callback) override; + void SetHDCPState(const DisplaySnapshot& output, + HDCPState state, + const SetHDCPStateCallback& callback) override; std::vector<ui::ColorCalibrationProfile> GetAvailableColorCalibrationProfiles( const DisplaySnapshot& output) override; bool SetColorCalibrationProfile( @@ -81,6 +94,9 @@ // return success regardless of the resolution. int max_configurable_pixels_; + bool get_hdcp_expectation_; + bool set_hdcp_expectation_; + // Result value of GetHDCPState(). HDCPState hdcp_state_;
diff --git a/ui/display/chromeos/update_display_configuration_task.cc b/ui/display/chromeos/update_display_configuration_task.cc index 2db4422..7b254fc 100644 --- a/ui/display/chromeos/update_display_configuration_task.cc +++ b/ui/display/chromeos/update_display_configuration_task.cc
@@ -5,6 +5,7 @@ #include "ui/display/chromeos/update_display_configuration_task.h" #include "ui/display/chromeos/configure_displays_task.h" +#include "ui/display/chromeos/display_layout_manager.h" #include "ui/display/chromeos/display_util.h" #include "ui/display/types/display_snapshot.h" #include "ui/display/types/native_display_delegate.h" @@ -13,7 +14,7 @@ UpdateDisplayConfigurationTask::UpdateDisplayConfigurationTask( NativeDisplayDelegate* delegate, - DisplayConfigurator::DisplayLayoutManager* layout_manager, + DisplayLayoutManager* layout_manager, MultipleDisplayState new_display_state, chromeos::DisplayPowerState new_power_state, int power_flags,
diff --git a/ui/display/chromeos/update_display_configuration_task.h b/ui/display/chromeos/update_display_configuration_task.h index c6447db5..7b8272e 100644 --- a/ui/display/chromeos/update_display_configuration_task.h +++ b/ui/display/chromeos/update_display_configuration_task.h
@@ -25,15 +25,14 @@ MultipleDisplayState /* new_display_state */, chromeos::DisplayPowerState /* new_power_state */)> ResponseCallback; - UpdateDisplayConfigurationTask( - NativeDisplayDelegate* delegate, - DisplayConfigurator::DisplayLayoutManager* layout_manager, - MultipleDisplayState new_display_state, - chromeos::DisplayPowerState new_power_state, - int power_flags, - uint32_t background_color_argb, - bool force_configure, - const ResponseCallback& callback); + UpdateDisplayConfigurationTask(NativeDisplayDelegate* delegate, + DisplayLayoutManager* layout_manager, + MultipleDisplayState new_display_state, + chromeos::DisplayPowerState new_power_state, + int power_flags, + uint32_t background_color_argb, + bool force_configure, + const ResponseCallback& callback); ~UpdateDisplayConfigurationTask(); void Run(); @@ -68,8 +67,8 @@ // Returns a display state based on the power state. MultipleDisplayState ChooseDisplayState() const; - NativeDisplayDelegate* delegate_; // Not owned. - DisplayConfigurator::DisplayLayoutManager* layout_manager_; // Not owned. + NativeDisplayDelegate* delegate_; // Not owned. + DisplayLayoutManager* layout_manager_; // Not owned. // Requested display state. MultipleDisplayState new_display_state_;
diff --git a/ui/display/chromeos/update_display_configuration_task_unittest.cc b/ui/display/chromeos/update_display_configuration_task_unittest.cc index b8a323b..b2904c73 100644 --- a/ui/display/chromeos/update_display_configuration_task_unittest.cc +++ b/ui/display/chromeos/update_display_configuration_task_unittest.cc
@@ -6,6 +6,7 @@ #include "base/message_loop/message_loop.h" #include "base/run_loop.h" #include "testing/gtest/include/gtest/gtest.h" +#include "ui/display/chromeos/display_layout_manager.h" #include "ui/display/chromeos/test/action_logger_util.h" #include "ui/display/chromeos/test/test_display_snapshot.h" #include "ui/display/chromeos/test/test_native_display_delegate.h" @@ -16,8 +17,7 @@ namespace { -class TestDisplayLayoutManager - : public DisplayConfigurator::DisplayLayoutManager { +class TestDisplayLayoutManager : public DisplayLayoutManager { public: TestDisplayLayoutManager() : should_mirror_(true), @@ -82,6 +82,15 @@ return true; } + DisplayConfigurator::DisplayStateList GetDisplayStates() const override { + NOTREACHED(); + return DisplayConfigurator::DisplayStateList(); + } + + bool IsMirroring() const override { + return display_state_ == MULTIPLE_DISPLAY_STATE_DUAL_MIRROR; + } + private: const DisplayMode* FindMirrorMode( const std::vector<DisplaySnapshot*>& displays) const {
diff --git a/ui/display/chromeos/x11/native_display_delegate_x11.cc b/ui/display/chromeos/x11/native_display_delegate_x11.cc index b5022ba..5c23e3f 100644 --- a/ui/display/chromeos/x11/native_display_delegate_x11.cc +++ b/ui/display/chromeos/x11/native_display_delegate_x11.cc
@@ -381,6 +381,14 @@ return display_snapshot; } +void NativeDisplayDelegateX11::GetHDCPState( + const DisplaySnapshot& output, + const GetHDCPStateCallback& callback) { + HDCPState state = HDCP_STATE_UNDESIRED; + bool success = GetHDCPState(output, &state); + callback.Run(success, state); +} + bool NativeDisplayDelegateX11::GetHDCPState(const DisplaySnapshot& output, HDCPState* state) { unsigned char* values = NULL; @@ -440,6 +448,13 @@ return true; } +void NativeDisplayDelegateX11::SetHDCPState( + const DisplaySnapshot& output, + HDCPState state, + const SetHDCPStateCallback& callback) { + callback.Run(SetHDCPState(output, state)); +} + bool NativeDisplayDelegateX11::SetHDCPState(const DisplaySnapshot& output, HDCPState state) { Atom name = XInternAtom(display_, kContentProtectionAtomName, False);
diff --git a/ui/display/chromeos/x11/native_display_delegate_x11.h b/ui/display/chromeos/x11/native_display_delegate_x11.h index 1d7d42a..036a121 100644 --- a/ui/display/chromeos/x11/native_display_delegate_x11.h +++ b/ui/display/chromeos/x11/native_display_delegate_x11.h
@@ -88,6 +88,11 @@ void CreateFrameBuffer(const gfx::Size& size) override; bool GetHDCPState(const DisplaySnapshot& output, HDCPState* state) override; bool SetHDCPState(const DisplaySnapshot& output, HDCPState state) override; + void GetHDCPState(const DisplaySnapshot& output, + const GetHDCPStateCallback& callback) override; + void SetHDCPState(const DisplaySnapshot& output, + HDCPState state, + const SetHDCPStateCallback& callback) override; std::vector<ColorCalibrationProfile> GetAvailableColorCalibrationProfiles( const DisplaySnapshot& output) override; bool SetColorCalibrationProfile(const DisplaySnapshot& output,
diff --git a/ui/display/display.gyp b/ui/display/display.gyp index ad17901..e8f45f7 100644 --- a/ui/display/display.gyp +++ b/ui/display/display.gyp
@@ -45,13 +45,18 @@ ], 'sources': [ # Note: file list duplicated in GN build. + 'chromeos/apply_content_protection_task.cc', + 'chromeos/apply_content_protection_task.h', 'chromeos/configure_displays_task.cc', 'chromeos/configure_displays_task.h', 'chromeos/display_configurator.cc', 'chromeos/display_configurator.h', + 'chromeos/display_layout_manager.h', 'chromeos/display_util.cc', 'chromeos/display_util.h', 'chromeos/ozone/display_configurator_ozone.cc', + 'chromeos/query_content_protection_task.cc', + 'chromeos/query_content_protection_task.h', 'chromeos/update_display_configuration_task.cc', 'chromeos/update_display_configuration_task.h', 'chromeos/x11/display_configurator_x11.cc', @@ -173,6 +178,8 @@ 'chromeos/test/action_logger.h', 'chromeos/test/action_logger_util.cc', 'chromeos/test/action_logger_util.h', + 'chromeos/test/test_display_layout_manager.cc', + 'chromeos/test/test_display_layout_manager.h', 'chromeos/test/test_native_display_delegate.cc', 'chromeos/test/test_native_display_delegate.h', ], @@ -191,8 +198,10 @@ '../..', ], 'sources': [ + 'chromeos/apply_content_protection_task_unittest.cc', 'chromeos/configure_displays_task_unittest.cc', 'chromeos/display_configurator_unittest.cc', + 'chromeos/query_content_protection_task_unittest.cc', 'chromeos/update_display_configuration_task_unittest.cc', 'chromeos/x11/display_util_x11_unittest.cc', 'chromeos/x11/native_display_event_dispatcher_x11_unittest.cc',
diff --git a/ui/display/types/native_display_delegate.h b/ui/display/types/native_display_delegate.h index 9019b71..aff8feb 100644 --- a/ui/display/types/native_display_delegate.h +++ b/ui/display/types/native_display_delegate.h
@@ -27,6 +27,8 @@ typedef base::Callback<void(const std::vector<ui::DisplaySnapshot*>&)> GetDisplaysCallback; typedef base::Callback<void(bool)> ConfigureCallback; +typedef base::Callback<void(bool, ui::HDCPState)> GetHDCPStateCallback; +typedef base::Callback<void(bool)> SetHDCPStateCallback; // Interface for classes that perform display configuration actions on behalf // of DisplayConfigurator. @@ -81,10 +83,15 @@ // Gets HDCP state of output. virtual bool GetHDCPState(const ui::DisplaySnapshot& output, ui::HDCPState* state) = 0; + virtual void GetHDCPState(const ui::DisplaySnapshot& output, + const GetHDCPStateCallback& callback) = 0; // Sets HDCP state of output. virtual bool SetHDCPState(const ui::DisplaySnapshot& output, ui::HDCPState state) = 0; + virtual void SetHDCPState(const ui::DisplaySnapshot& output, + ui::HDCPState state, + const SetHDCPStateCallback& callback) = 0; // Gets the available list of color calibrations. virtual std::vector<ui::ColorCalibrationProfile>
diff --git a/ui/events/BUILD.gn b/ui/events/BUILD.gn index 22f49b6..208cc183 100644 --- a/ui/events/BUILD.gn +++ b/ui/events/BUILD.gn
@@ -33,6 +33,7 @@ "gesture_event_details.h", "gestures/fling_curve.cc", "gestures/fling_curve.h", + "keycodes/dom_us_layout_data.h", "keycodes/keyboard_code_conversion.cc", "keycodes/keyboard_code_conversion.h", "keycodes/keyboard_code_conversion_android.cc", @@ -297,6 +298,7 @@ "gestures/blink/web_gesture_curve_impl_unittest.cc", "gestures/fling_curve_unittest.cc", "keycodes/dom4/keycode_converter_unittest.cc", + "keycodes/keyboard_code_conversion_unittest.cc", "latency_info_unittest.cc", "platform/platform_event_source_unittest.cc", "x/events_x_unittest.cc",
diff --git a/ui/events/cocoa/events_mac.mm b/ui/events/cocoa/events_mac.mm index 64eb972e..e68c755 100644 --- a/ui/events/cocoa/events_mac.mm +++ b/ui/events/cocoa/events_mac.mm
@@ -128,6 +128,8 @@ case NSOtherMouseUp: case NSOtherMouseDragged: return EF_MIDDLE_MOUSE_BUTTON; + default: + break; } return 0; }
diff --git a/ui/events/event.cc b/ui/events/event.cc index 54440199..4649e24d 100644 --- a/ui/events/event.cc +++ b/ui/events/event.cc
@@ -697,7 +697,7 @@ int flags) : Event(type, EventTimeForNow(), flags), key_code_(key_code), - code_(DomCode::NONE), + code_(UsLayoutKeyboardCodeToDomCode(key_code)), is_char_(false), platform_keycode_(0), key_(DomKey::NONE), @@ -786,34 +786,49 @@ key_ = DomKey::UNIDENTIFIED; return; } + ui::DomCode code = code_; + if (code == DomCode::NONE) { + // Catch old code that tries to do layout without a physical key, and try + // to recover using the KeyboardCode. Once key events are fully defined + // on construction (see TODO in event.h) this will go away. + LOG(WARNING) << "DomCode::NONE keycode=" << key_code_; + code = UsLayoutKeyboardCodeToDomCode(key_code_); + if (code == DomCode::NONE) { + key_ = DomKey::UNIDENTIFIED; + return; + } + } + KeyboardCode dummy_key_code; #if defined(OS_WIN) - // Native Windows character events always have is_char_ == true, - // so this is a synthetic or native keystroke event. - // Therefore, perform only the fallback action. - GetMeaningFromKeyCode(key_code_, flags(), &key_, &character_); +// Native Windows character events always have is_char_ == true, +// so this is a synthetic or native keystroke event. +// Therefore, perform only the fallback action. #elif defined(USE_X11) // When a control key is held, prefer ASCII characters to non ASCII // characters in order to use it for shortcut keys. GetCharacterFromKeyCode // returns 'a' for VKEY_A even if the key is actually bound to 'à' in X11. // GetCharacterFromXEvent returns 'à' in that case. - character_ = (IsControlDown() || !native_event()) ? - GetCharacterFromKeyCode(key_code_, flags()) : - GetCharacterFromXEvent(native_event()); - // TODO(kpschoedel): set key_ field for X11. + if (!IsControlDown() && native_event()) { + character_ = GetCharacterFromXEvent(native_event()); + // TODO(kpschoedel): set key_ field for X11. + return; + } #elif defined(USE_OZONE) - KeyboardCode key_code; - if (!KeyboardLayoutEngineManager::GetKeyboardLayoutEngine()->Lookup( - code_, flags(), &key_, &character_, &key_code, &platform_keycode_)) { - GetMeaningFromKeyCode(key_code_, flags(), &key_, &character_); + if (KeyboardLayoutEngineManager::GetKeyboardLayoutEngine()->Lookup( + code, flags(), &key_, &character_, &dummy_key_code, + &platform_keycode_)) { + return; } #else if (native_event()) { DCHECK(EventTypeFromNative(native_event()) == ET_KEY_PRESSED || EventTypeFromNative(native_event()) == ET_KEY_RELEASED); } - // TODO(kpschoedel): revise to use DOM code_ instead of Windows key_code_ - GetMeaningFromKeyCode(key_code_, flags(), &key_, &character_); #endif + if (!DomCodeToUsLayoutMeaning(code, flags(), &key_, &character_, + &dummy_key_code)) { + key_ = DomKey::UNIDENTIFIED; + } } DomKey KeyEvent::GetDomKey() const { @@ -832,9 +847,11 @@ base::char16 KeyEvent::GetText() const { if ((flags() & EF_CONTROL_DOWN) != 0) { - // TODO(kpschoedel): revise to use DOM code_ instead of Windows key_code_ - return GetControlCharacterForKeycode(key_code_, - (flags() & EF_SHIFT_DOWN) != 0); + base::char16 character; + ui::DomKey key; + ui::KeyboardCode key_code; + if (DomCodeToControlCharacter(code_, flags(), &key, &character, &key_code)) + return character; } return GetUnmodifiedText(); }
diff --git a/ui/events/event.h b/ui/events/event.h index 6a27b0e9..349ed55c 100644 --- a/ui/events/event.h +++ b/ui/events/event.h
@@ -779,7 +779,7 @@ // TODO(kpschoedel): refactor so that key_ and character_ are not mutable. // This requires defining the KeyEvent completely at construction rather // than lazily under GetCharacter(), which likely also means removing - // the two 'incomplete' constructors. + // the two 'incomplete' constructors. crbug.com/444045 // // DOM KeyboardEvent |key| // http://www.w3.org/TR/DOM-Level-3-Events-key/
diff --git a/ui/events/event_unittest.cc b/ui/events/event_unittest.cc index 1297f30..6d94812 100644 --- a/ui/events/event_unittest.cc +++ b/ui/events/event_unittest.cc
@@ -367,11 +367,10 @@ EXPECT_EQ(kCodeForSpace, key.GetCodeString()); } { - // If the synthetic event is initialized without code, it returns - // an empty string. - // TODO(komatsu): Fill a fallback value assuming the US keyboard layout. + // If the synthetic event is initialized without code, the code is + // determined from the KeyboardCode assuming a US keyboard layout. KeyEvent key(ET_KEY_PRESSED, VKEY_SPACE, EF_NONE); - EXPECT_TRUE(key.GetCodeString().empty()); + EXPECT_EQ(kCodeForSpace, key.GetCodeString()); } #if defined(USE_X11) {
diff --git a/ui/events/event_utils.cc b/ui/events/event_utils.cc index 2e3517e..021ac27 100644 --- a/ui/events/event_utils.cc +++ b/ui/events/event_utils.cc
@@ -63,51 +63,6 @@ return event.Pass(); } -// From third_party/WebKit/Source/web/gtk/WebInputEventFactory.cpp: -base::char16 GetControlCharacterForKeycode(int windows_key_code, bool shift) { - if (windows_key_code >= ui::VKEY_A && - windows_key_code <= ui::VKEY_Z) { - // ctrl-A ~ ctrl-Z map to \x01 ~ \x1A - return windows_key_code - ui::VKEY_A + 1; - } - if (shift) { - // following graphics chars require shift key to input. - switch (windows_key_code) { - // ctrl-@ maps to \x00 (Null byte) - case ui::VKEY_2: - return 0; - // ctrl-^ maps to \x1E (Record separator, Information separator two) - case ui::VKEY_6: - return 0x1E; - // ctrl-_ maps to \x1F (Unit separator, Information separator one) - case ui::VKEY_OEM_MINUS: - return 0x1F; - // Returns 0 for all other keys to avoid inputting unexpected chars. - default: - break; - } - } else { - switch (windows_key_code) { - // ctrl-[ maps to \x1B (Escape) - case ui::VKEY_OEM_4: - return 0x1B; - // ctrl-\ maps to \x1C (File separator, Information separator four) - case ui::VKEY_OEM_5: - return 0x1C; - // ctrl-] maps to \x1D (Group separator, Information separator three) - case ui::VKEY_OEM_6: - return 0x1D; - // ctrl-Enter maps to \x0A (Line feed) - case ui::VKEY_RETURN: - return 0x0A; - // Returns 0 for all other keys to avoid inputting unexpected chars. - default: - break; - } - } - return 0; -} - int RegisterCustomEventType() { return ++g_custom_event_types; }
diff --git a/ui/events/event_utils.h b/ui/events/event_utils.h index 1649bd7..e9c9093a 100644 --- a/ui/events/event_utils.h +++ b/ui/events/event_utils.h
@@ -84,14 +84,11 @@ // keyboard) from a native event. EVENTS_EXPORT DomCode CodeFromNative(const base::NativeEvent& native_event); -// Returns the platform related key code. For X11, it is xksym value. +// Returns the platform related key code (interpretation, not scan code). +// For X11 and xkbcommon, this is the KeySym value. EVENTS_EXPORT uint32 PlatformKeycodeFromNative( const base::NativeEvent& native_event); -// Returns a control character sequences from a |windows_key_code|. -EVENTS_EXPORT base::char16 GetControlCharacterForKeycode(int windows_key_code, - bool shift); - // Returns true if the keyboard event is a character event rather than // a keystroke event. EVENTS_EXPORT bool IsCharFromNative(const base::NativeEvent& native_event);
diff --git a/ui/events/events.gyp b/ui/events/events.gyp index 9e63434b..2f5757b 100644 --- a/ui/events/events.gyp +++ b/ui/events/events.gyp
@@ -52,6 +52,7 @@ 'gesture_event_details.h', 'gestures/fling_curve.cc', 'gestures/fling_curve.h', + 'keycodes/dom_us_layout_data.h', 'keycodes/keyboard_code_conversion.cc', 'keycodes/keyboard_code_conversion.h', 'keycodes/keyboard_code_conversion_android.cc', @@ -373,6 +374,7 @@ 'gestures/gesture_provider_aura_unittest.cc', 'gestures/motion_event_aura_unittest.cc', 'keycodes/dom4/keycode_converter_unittest.cc', + 'keycodes/keyboard_code_conversion_unittest.cc', 'latency_info_unittest.cc', 'platform/platform_event_source_unittest.cc', 'x/events_x_unittest.cc',
diff --git a/ui/events/ipc/latency_info_param_traits.h b/ui/events/ipc/latency_info_param_traits.h index 289ecb4..e33a044 100644 --- a/ui/events/ipc/latency_info_param_traits.h +++ b/ui/events/ipc/latency_info_param_traits.h
@@ -33,6 +33,7 @@ IPC_STRUCT_TRAITS_MEMBER(input_coordinates_size) IPC_STRUCT_TRAITS_MEMBER(input_coordinates[0]) IPC_STRUCT_TRAITS_MEMBER(input_coordinates[1]) + IPC_STRUCT_TRAITS_MEMBER(trace_name) IPC_STRUCT_TRAITS_END() #endif // UI_EVENTS_IPC_LATENCY_INFO_PARAM_TRAITS_H_
diff --git a/ui/events/keycodes/dom4/keycode_converter.cc b/ui/events/keycodes/dom4/keycode_converter.cc index 7c81862..d9a26a2 100644 --- a/ui/events/keycodes/dom4/keycode_converter.cc +++ b/ui/events/keycodes/dom4/keycode_converter.cc
@@ -4,6 +4,7 @@ #include "ui/events/keycodes/dom4/keycode_converter.h" +#include "base/logging.h" #include "ui/events/keycodes/dom3/dom_code.h" #include "ui/events/keycodes/dom3/dom_key.h" @@ -118,14 +119,17 @@ // static DomCode KeycodeConverter::CodeStringToDomCode(const char* code) { - if (!code || !*code) + if (!code || !*code) { + LOG(WARNING) << "empty code string"; return DomCode::NONE; + } for (size_t i = 0; i < kKeycodeMapEntries; ++i) { if (usb_keycode_map[i].code && strcmp(usb_keycode_map[i].code, code) == 0) { return static_cast<DomCode>(usb_keycode_map[i].usb_keycode); } } + LOG(WARNING) << "unrecognized code string '" << code << "'"; return DomCode::NONE; }
diff --git a/ui/events/keycodes/dom4/keycode_converter_data.h b/ui/events/keycodes/dom4/keycode_converter_data.h index 0d9fccd..2f7712ed 100644 --- a/ui/events/keycodes/dom4/keycode_converter_data.h +++ b/ui/events/keycodes/dom4/keycode_converter_data.h
@@ -399,6 +399,12 @@ USB_KEYMAP(0x0c0075, 0x00fc, 0x0000, 0xffff, NULL, BRIGHTNESS_AUTO), // USB XKB Win Mac + //USB_KEYMAP(0x0c00b0, 0x00d7, 0x????, 0x????, "MediaPlay", MEDIA_PLAY), + //USB_KEYMAP(0x0c00b1, 0x007f, 0x????, 0x????, "MediaPause", MEDIA_PAUSE), + //USB_KEYMAP(0x0c00b2, 0x00af, 0x????, 0x????, "MediaRecord", MEDIA_RECORD), + //USB_KEYMAP(0x0c00b3, 0x00d8, 0x????, 0x????, "MediaFastForward", + // MEDIA_FAST_FORWARD), + //USB_KEYMAP(0x0c00b4, 0x00b0, 0x????, 0x????, "MediaRewind", MEDIA_REWIND), USB_KEYMAP(0x0c00b5, 0x0000, 0xe019, 0xffff, "MediaTrackNext", MEDIA_TRACK_NEXT), USB_KEYMAP(0x0c00b6, 0x0000, 0xe010, 0xffff, "MediaTrackPrevious", @@ -441,6 +447,8 @@ USB_KEYMAP(0x0c01b4, 0x0098, 0x0000, 0xffff, NULL, LAUNCH_FILE_BROWSER), // USB#0x0c01b7: AL Audio Browser //USB_KEYMAP(0x0c01b7, 0x0190, 0x0000, 0xffff, NULL, LAUNCH_AUDIO_BROWSER), + // USB#0x0c0208: AC Print + //USB_KEYMAP(0x0c0208, 0x00da, 0x0000, 0xffff, NULL, PRINT), // USB#0x0c0221: AC_Search USB_KEYMAP(0x0c0221, 0x0000, 0xe065, 0xffff, "BrowserSearch", BROWSER_SEARCH), // USB#0x0c0223: AC_Home
diff --git a/ui/events/keycodes/dom_us_layout_data.h b/ui/events/keycodes/dom_us_layout_data.h new file mode 100644 index 0000000..c85aef2b --- /dev/null +++ b/ui/events/keycodes/dom_us_layout_data.h
@@ -0,0 +1,624 @@ +// 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 UI_EVENTS_KEYCODES_DOM_US_LAYOUT_DATA_H_ +#define UI_EVENTS_KEYCODES_DOM_US_LAYOUT_DATA_H_ + +namespace ui { + +// This table maps a DomCode to a printable character, assuming US layout. +const struct PrintableCodeEntry { + DomCode dom_code; + base::char16 character[2]; // normal, shift +} kPrintableCodeMap[] = { + {DomCode::KEY_A, {'a', 'A'}}, + {DomCode::KEY_B, {'b', 'B'}}, + {DomCode::KEY_C, {'c', 'C'}}, + {DomCode::KEY_D, {'d', 'D'}}, + {DomCode::KEY_E, {'e', 'E'}}, + {DomCode::KEY_F, {'f', 'F'}}, + {DomCode::KEY_G, {'g', 'G'}}, + {DomCode::KEY_H, {'h', 'H'}}, + {DomCode::KEY_I, {'i', 'I'}}, + {DomCode::KEY_J, {'j', 'J'}}, + {DomCode::KEY_K, {'k', 'K'}}, + {DomCode::KEY_L, {'l', 'L'}}, + {DomCode::KEY_M, {'m', 'M'}}, + {DomCode::KEY_N, {'n', 'N'}}, + {DomCode::KEY_O, {'o', 'O'}}, + {DomCode::KEY_P, {'p', 'P'}}, + {DomCode::KEY_Q, {'q', 'Q'}}, + {DomCode::KEY_R, {'r', 'R'}}, + {DomCode::KEY_S, {'s', 'S'}}, + {DomCode::KEY_T, {'t', 'T'}}, + {DomCode::KEY_U, {'u', 'U'}}, + {DomCode::KEY_V, {'v', 'V'}}, + {DomCode::KEY_W, {'w', 'W'}}, + {DomCode::KEY_X, {'x', 'X'}}, + {DomCode::KEY_Y, {'y', 'Y'}}, + {DomCode::KEY_Z, {'z', 'Z'}}, + {DomCode::DIGIT1, {'1', '!'}}, + {DomCode::DIGIT2, {'2', '@'}}, + {DomCode::DIGIT3, {'3', '#'}}, + {DomCode::DIGIT4, {'4', '$'}}, + {DomCode::DIGIT5, {'5', '%'}}, + {DomCode::DIGIT6, {'6', '^'}}, + {DomCode::DIGIT7, {'7', '&'}}, + {DomCode::DIGIT8, {'8', '*'}}, + {DomCode::DIGIT9, {'9', '('}}, + {DomCode::DIGIT0, {'0', ')'}}, + {DomCode::SPACE, {' ', ' '}}, + {DomCode::MINUS, {'-', '_'}}, + {DomCode::EQUAL, {'=', '+'}}, + {DomCode::BRACKET_LEFT, {'[', '{'}}, + {DomCode::BRACKET_RIGHT, {']', '}'}}, + {DomCode::BACKSLASH, {'\\', '|'}}, + {DomCode::SEMICOLON, {';', ':'}}, + {DomCode::QUOTE, {'\'', '"'}}, + {DomCode::BACKQUOTE, {'`', '~'}}, + {DomCode::COMMA, {',', '<'}}, + {DomCode::PERIOD, {'.', '>'}}, + {DomCode::SLASH, {'/', '?'}}, + {DomCode::INTL_BACKSLASH, {'\\', '|'}}, + {DomCode::INTL_YEN, {0x00A5, '|'}}, + {DomCode::NUMPAD_DIVIDE, {'/', '/'}}, + {DomCode::NUMPAD_MULTIPLY, {'*', '*'}}, + {DomCode::NUMPAD_SUBTRACT, {'-', '-'}}, + {DomCode::NUMPAD_ADD, {'+', '+'}}, + {DomCode::NUMPAD1, {'1', '1'}}, + {DomCode::NUMPAD2, {'2', '2'}}, + {DomCode::NUMPAD3, {'3', '3'}}, + {DomCode::NUMPAD4, {'4', '4'}}, + {DomCode::NUMPAD5, {'5', '5'}}, + {DomCode::NUMPAD6, {'6', '6'}}, + {DomCode::NUMPAD7, {'7', '7'}}, + {DomCode::NUMPAD8, {'8', '8'}}, + {DomCode::NUMPAD9, {'9', '9'}}, + {DomCode::NUMPAD0, {'0', '0'}}, + {DomCode::NUMPAD_DECIMAL, {'.', '.'}}, + {DomCode::NUMPAD_EQUAL, {'=', '='}}, + {DomCode::NUMPAD_COMMA, {',', ','}}, + {DomCode::NUMPAD_PAREN_LEFT, {'(', '('}}, + {DomCode::NUMPAD_PAREN_RIGHT, {')', ')'}}, + {DomCode::NUMPAD_SIGN_CHANGE, {0x00B1, 0x00B1}}, +}; + +// This table maps a DomCode to a DomKey, assuming US keyboard layout. +const struct NonPrintableCodeEntry { + DomCode dom_code; + DomKey dom_key; + base::char16 character; +} kNonPrintableCodeMap[] = { + {DomCode::ABORT, DomKey::CANCEL}, + {DomCode::AGAIN, DomKey::AGAIN}, + {DomCode::ALT_LEFT, DomKey::ALT}, + {DomCode::ALT_RIGHT, DomKey::ALT}, + {DomCode::ARROW_DOWN, DomKey::ARROW_DOWN}, + {DomCode::ARROW_LEFT, DomKey::ARROW_LEFT}, + {DomCode::ARROW_RIGHT, DomKey::ARROW_RIGHT}, + {DomCode::ARROW_UP, DomKey::ARROW_UP}, + {DomCode::BACKSPACE, DomKey::BACKSPACE, 0x0008}, + {DomCode::BRIGHTNESS_DOWN, DomKey::BRIGHTNESS_DOWN}, + {DomCode::BRIGHTNESS_UP, DomKey::BRIGHTNESS_UP}, + // {DomCode::BRIGHTNESS_AUTO, DomKey::_} + // {DomCode::BRIGHTNESS_MAXIMUM, DomKey::_} + // {DomCode::BRIGHTNESS_MINIMIUM, DomKey::_} + // {DomCode::BRIGHTNESS_TOGGLE, DomKey::_} + {DomCode::BROWSER_BACK, DomKey::BROWSER_BACK}, + {DomCode::BROWSER_FAVORITES, DomKey::BROWSER_FAVORITES}, + {DomCode::BROWSER_FORWARD, DomKey::BROWSER_FORWARD}, + {DomCode::BROWSER_HOME, DomKey::BROWSER_HOME}, + {DomCode::BROWSER_REFRESH, DomKey::BROWSER_REFRESH}, + {DomCode::BROWSER_SEARCH, DomKey::BROWSER_SEARCH}, + {DomCode::BROWSER_STOP, DomKey::BROWSER_STOP}, + {DomCode::CAPS_LOCK, DomKey::CAPS_LOCK}, + {DomCode::CONTEXT_MENU, DomKey::CONTEXT_MENU}, + {DomCode::CONTROL_LEFT, DomKey::CONTROL}, + {DomCode::CONTROL_RIGHT, DomKey::CONTROL}, + {DomCode::CONVERT, DomKey::CONVERT}, + {DomCode::COPY, DomKey::COPY}, + {DomCode::CUT, DomKey::CUT}, + {DomCode::DEL, DomKey::DEL, 0x007F}, + {DomCode::EJECT, DomKey::EJECT}, + {DomCode::END, DomKey::END}, + {DomCode::ENTER, DomKey::ENTER, 0x000D}, + {DomCode::ESCAPE, DomKey::ESCAPE, 0x001B}, + {DomCode::F1, DomKey::F1}, + {DomCode::F2, DomKey::F2}, + {DomCode::F3, DomKey::F3}, + {DomCode::F4, DomKey::F4}, + {DomCode::F5, DomKey::F5}, + {DomCode::F6, DomKey::F6}, + {DomCode::F7, DomKey::F7}, + {DomCode::F8, DomKey::F8}, + {DomCode::F9, DomKey::F9}, + {DomCode::F10, DomKey::F10}, + {DomCode::F11, DomKey::F11}, + {DomCode::F12, DomKey::F12}, + {DomCode::F13, DomKey::F13}, + {DomCode::F14, DomKey::F14}, + {DomCode::F15, DomKey::F15}, + {DomCode::F16, DomKey::F16}, + {DomCode::F17, DomKey::F17}, + {DomCode::F18, DomKey::F18}, + {DomCode::F19, DomKey::F19}, + {DomCode::F20, DomKey::F20}, + {DomCode::F21, DomKey::F21}, + {DomCode::F22, DomKey::F22}, + {DomCode::F23, DomKey::F23}, + {DomCode::F24, DomKey::F24}, + {DomCode::FIND, DomKey::FIND}, + {DomCode::FN, DomKey::FN}, + {DomCode::FN_LOCK, DomKey::FN_LOCK}, + {DomCode::HELP, DomKey::HELP}, + {DomCode::HOME, DomKey::HOME}, + {DomCode::HYPER, DomKey::HYPER}, + {DomCode::INSERT, DomKey::INSERT}, + // {DomCode::INTL_RO, DomKey::_} + {DomCode::KANA_MODE, DomKey::KANA_MODE}, + {DomCode::LANG1, DomKey::HANGUL_MODE}, + {DomCode::LANG2, DomKey::HANJA_MODE}, + {DomCode::LANG3, DomKey::KATAKANA}, + {DomCode::LANG4, DomKey::HIRAGANA}, + {DomCode::LANG5, DomKey::ZENKAKU_HANKAKU}, + {DomCode::LAUNCH_APP1, DomKey::LAUNCH_MY_COMPUTER}, + {DomCode::LAUNCH_APP2, DomKey::LAUNCH_CALCULATOR}, + {DomCode::LAUNCH_MAIL, DomKey::LAUNCH_MAIL}, + {DomCode::LAUNCH_SCREEN_SAVER, DomKey::LAUNCH_SCREEN_SAVER}, + // {DomCode::LAUNCH_DOCUMENTS, DomKey::_} + // {DomCode::LAUNCH_FILE_BROWSER, DomKey::_} + // {DomCode::LAUNCH_KEYBOARD_LAYOUT, DomKey::_} + {DomCode::LOCK_SCREEN, DomKey::LAUNCH_SCREEN_SAVER}, + {DomCode::MAIL_FORWARD, DomKey::MAIL_FORWARD}, + {DomCode::MAIL_REPLY, DomKey::MAIL_REPLY}, + {DomCode::MAIL_SEND, DomKey::MAIL_SEND}, + {DomCode::MEDIA_PLAY_PAUSE, DomKey::MEDIA_PLAY_PAUSE}, + {DomCode::MEDIA_SELECT, DomKey::MEDIA_SELECT}, + {DomCode::MEDIA_STOP, DomKey::MEDIA_STOP}, + {DomCode::MEDIA_TRACK_NEXT, DomKey::MEDIA_TRACK_NEXT}, + {DomCode::MEDIA_TRACK_PREVIOUS, DomKey::MEDIA_TRACK_PREVIOUS}, + // {DomCode::MENU, DomKey::_} + {DomCode::NON_CONVERT, DomKey::NON_CONVERT}, + {DomCode::NUM_LOCK, DomKey::NUM_LOCK}, + {DomCode::NUMPAD_BACKSPACE, DomKey::BACKSPACE, 0x0008}, + {DomCode::NUMPAD_CLEAR, DomKey::CLEAR}, + {DomCode::NUMPAD_ENTER, DomKey::ENTER, 0x000D}, + // {DomCode::NUMPAD_CLEAR_ENTRY, DomKey::_} + // {DomCode::NUMPAD_MEMORY_ADD, DomKey::_} + // {DomCode::NUMPAD_MEMORY_CLEAR, DomKey::_} + // {DomCode::NUMPAD_MEMORY_RECALL, DomKey::_} + // {DomCode::NUMPAD_MEMORY_STORE, DomKey::_} + // {DomCode::NUMPAD_MEMORY_SUBTRACT, DomKey::_} + {DomCode::OPEN, DomKey::OPEN}, + {DomCode::OS_LEFT, DomKey::OS}, + {DomCode::OS_RIGHT, DomKey::OS}, + {DomCode::PAGE_DOWN, DomKey::PAGE_DOWN}, + {DomCode::PAGE_UP, DomKey::PAGE_UP}, + {DomCode::PASTE, DomKey::PASTE}, + {DomCode::PAUSE, DomKey::PAUSE}, + {DomCode::POWER, DomKey::POWER}, + {DomCode::PRINT_SCREEN, DomKey::PRINT_SCREEN}, + {DomCode::PROPS, DomKey::PROPS}, + {DomCode::SCROLL_LOCK, DomKey::SCROLL_LOCK}, + {DomCode::SELECT, DomKey::SELECT}, + // {DomCode::SELECT_TASK, DomKey::_} + {DomCode::SHIFT_LEFT, DomKey::SHIFT}, + {DomCode::SHIFT_RIGHT, DomKey::SHIFT}, + {DomCode::SUPER, DomKey::SUPER}, + {DomCode::TAB, DomKey::TAB, 0x0009}, + {DomCode::UNDO, DomKey::UNDO}, + // {DomCode::VOICE_COMMAND, DomKey::_} + {DomCode::VOLUME_DOWN, DomKey::VOLUME_DOWN}, + {DomCode::VOLUME_MUTE, DomKey::VOLUME_MUTE}, + {DomCode::VOLUME_UP, DomKey::VOLUME_UP}, + {DomCode::WAKE_UP, DomKey::WAKE_UP}, + {DomCode::ZOOM_TOGGLE, DomKey::ZOOM_TOGGLE}, +}; + +// This table maps a DomKey to a non-located KeyboardCode. +const struct DomKeyToKeyboardCodeEntry { + DomKey dom_key; + KeyboardCode key_code; +} kDomKeyToKeyboardCodeMap[] = { + // No value. + {DomKey::NONE, VKEY_UNKNOWN}, + // Special Key Values + // http://www.w3.org/TR/DOM-Level-3-Events-key/#keys-special + {DomKey::UNIDENTIFIED, VKEY_UNKNOWN}, + // Modifier Keys + // http://www.w3.org/TR/DOM-Level-3-Events-key/#keys-modifier + {DomKey::ALT, VKEY_MENU}, + {DomKey::ALT_GRAPH, VKEY_ALTGR}, + {DomKey::CAPS_LOCK, VKEY_CAPITAL}, + {DomKey::CONTROL, VKEY_CONTROL}, + {DomKey::NUM_LOCK, VKEY_NUMLOCK}, + {DomKey::OS, VKEY_LWIN}, + {DomKey::SCROLL_LOCK, VKEY_SCROLL}, + {DomKey::SHIFT, VKEY_SHIFT}, + // Whitespace Keys + // http://www.w3.org/TR/DOM-Level-3-Events-key/#keys-whitespace + {DomKey::ENTER, VKEY_RETURN}, + {DomKey::SEPARATOR, VKEY_SEPARATOR}, + {DomKey::TAB, VKEY_TAB}, + // Navigation Keys + // http://www.w3.org/TR/DOM-Level-3-Events-key/#keys-navigation + {DomKey::ARROW_DOWN, VKEY_DOWN}, + {DomKey::ARROW_LEFT, VKEY_LEFT}, + {DomKey::ARROW_RIGHT, VKEY_RIGHT}, + {DomKey::ARROW_UP, VKEY_UP}, + {DomKey::END, VKEY_END}, + {DomKey::HOME, VKEY_HOME}, + {DomKey::PAGE_DOWN, VKEY_NEXT}, + {DomKey::PAGE_UP, VKEY_PRIOR}, + // Editing Keys + // http://www.w3.org/TR/DOM-Level-3-Events-key/#keys-editing + {DomKey::BACKSPACE, VKEY_BACK}, + {DomKey::CLEAR, VKEY_CLEAR}, + {DomKey::CR_SEL, VKEY_CRSEL}, + {DomKey::DEL, VKEY_DELETE}, + {DomKey::ERASE_EOF, VKEY_EREOF}, + {DomKey::EX_SEL, VKEY_EXSEL}, + {DomKey::INSERT, VKEY_INSERT}, + // UI Keys + // http://www.w3.org/TR/DOM-Level-3-Events-key/#keys-ui + {DomKey::ACCEPT, VKEY_ACCEPT}, + {DomKey::ATTN, VKEY_ATTN}, + {DomKey::CONTEXT_MENU, VKEY_APPS}, + {DomKey::ESCAPE, VKEY_ESCAPE}, + {DomKey::EXECUTE, VKEY_EXECUTE}, + {DomKey::HELP, VKEY_HELP}, + {DomKey::PAUSE, VKEY_PAUSE}, + {DomKey::PLAY, VKEY_PLAY}, + {DomKey::SELECT, VKEY_SELECT}, + // Device Keys + // http://www.w3.org/TR/DOM-Level-3-Events-key/#keys-device +#if defined(OS_POSIX) + {DomKey::BRIGHTNESS_DOWN, VKEY_BRIGHTNESS_DOWN}, + {DomKey::BRIGHTNESS_UP, VKEY_BRIGHTNESS_UP}, + {DomKey::POWER, VKEY_POWER}, +#endif + {DomKey::PRINT_SCREEN, VKEY_SNAPSHOT}, +// IME and Composition Keys +// http://www.w3.org/TR/DOM-Level-3-Events-key/#keys-composition +#if defined(OS_POSIX) + {DomKey::COMPOSE, VKEY_COMPOSE}, +#endif + {DomKey::CONVERT, VKEY_CONVERT}, + {DomKey::FINAL_MODE, VKEY_FINAL}, + {DomKey::MODE_CHANGE, VKEY_MODECHANGE}, + {DomKey::NON_CONVERT, VKEY_NONCONVERT}, + {DomKey::PROCESS, VKEY_PROCESSKEY}, + // Keys specific to Korean keyboards + {DomKey::HANGUL_MODE, VKEY_HANGUL}, + {DomKey::HANJA_MODE, VKEY_HANJA}, + {DomKey::JUNJA_MODE, VKEY_JUNJA}, + // Keys specific to Japanese keyboards + {DomKey::HANKAKU, VKEY_DBE_SBCSCHAR}, + {DomKey::KANA_MODE, VKEY_KANA}, + {DomKey::KANJI_MODE, VKEY_KANJI}, + {DomKey::ZENKAKU, VKEY_DBE_DBCSCHAR}, + {DomKey::ZENKAKU_HANKAKU, VKEY_DBE_DBCSCHAR}, + // General-Purpose Function Keys + // http://www.w3.org/TR/DOM-Level-3-Events-key/#keys-function + {DomKey::F1, VKEY_F1}, + {DomKey::F2, VKEY_F2}, + {DomKey::F3, VKEY_F3}, + {DomKey::F4, VKEY_F4}, + {DomKey::F5, VKEY_F5}, + {DomKey::F6, VKEY_F6}, + {DomKey::F7, VKEY_F7}, + {DomKey::F8, VKEY_F8}, + {DomKey::F9, VKEY_F9}, + {DomKey::F10, VKEY_F10}, + {DomKey::F11, VKEY_F11}, + {DomKey::F12, VKEY_F12}, + {DomKey::F13, VKEY_F13}, + {DomKey::F14, VKEY_F14}, + {DomKey::F15, VKEY_F15}, + {DomKey::F16, VKEY_F16}, + {DomKey::F17, VKEY_F17}, + {DomKey::F18, VKEY_F18}, + {DomKey::F19, VKEY_F19}, + {DomKey::F20, VKEY_F20}, + {DomKey::F21, VKEY_F21}, + {DomKey::F22, VKEY_F22}, + {DomKey::F23, VKEY_F23}, + {DomKey::F24, VKEY_F24}, + // Multimedia Keys + // http://www.w3.org/TR/DOM-Level-3-Events-key/#keys-multimedia + {DomKey::MEDIA_PLAY_PAUSE, VKEY_MEDIA_PLAY_PAUSE}, + {DomKey::MEDIA_SELECT, VKEY_MEDIA_LAUNCH_MEDIA_SELECT}, + {DomKey::MEDIA_STOP, VKEY_MEDIA_STOP}, + {DomKey::MEDIA_TRACK_NEXT, VKEY_MEDIA_NEXT_TRACK}, + {DomKey::MEDIA_TRACK_PREVIOUS, VKEY_MEDIA_PREV_TRACK}, + {DomKey::PRINT, VKEY_PRINT}, + {DomKey::VOLUME_DOWN, VKEY_VOLUME_DOWN}, + {DomKey::VOLUME_MUTE, VKEY_VOLUME_MUTE}, + {DomKey::VOLUME_UP, VKEY_VOLUME_UP}, + // Application Keys + // http://www.w3.org/TR/DOM-Level-3-Events-key/#keys-apps + {DomKey::LAUNCH_CALCULATOR, VKEY_MEDIA_LAUNCH_APP2}, + {DomKey::LAUNCH_MAIL, VKEY_MEDIA_LAUNCH_MAIL}, + {DomKey::LAUNCH_MY_COMPUTER, VKEY_MEDIA_LAUNCH_APP1}, + // Browser Keys + // http://www.w3.org/TR/DOM-Level-3-Events-key/#keys-browser + {DomKey::BROWSER_BACK, VKEY_BROWSER_BACK}, + {DomKey::BROWSER_FAVORITES, VKEY_BROWSER_FAVORITES}, + {DomKey::BROWSER_FORWARD, VKEY_BROWSER_FORWARD}, + {DomKey::BROWSER_HOME, VKEY_BROWSER_HOME}, + {DomKey::BROWSER_REFRESH, VKEY_BROWSER_REFRESH}, + {DomKey::BROWSER_SEARCH, VKEY_BROWSER_SEARCH}, + {DomKey::BROWSER_STOP, VKEY_BROWSER_STOP}, + // Media Controller Keys + // http://www.w3.org/TR/DOM-Level-3-Events-key/#keys-media-controller +#if defined(OS_POSIX) + {DomKey::MEDIA_FAST_FORWARD, VKEY_OEM_104}, +#endif + {DomKey::MEDIA_PLAY, VKEY_PLAY}, +#if defined(OS_POSIX) + {DomKey::MEDIA_REWIND, VKEY_OEM_103}, +#endif + {DomKey::ZOOM_TOGGLE, VKEY_ZOOM}, +}; + +// This table, used by DomCodeToUsLayoutKeyboardCode() and +// UsLayoutKeyboardCodeToDomCode(), maps between DOM Level 3 .code values +// and legacy Windows-based VKEY values, where the VKEYs are interpreted +// positionally (located) following a base US English layout. +const struct DomCodeToKeyboardCodeEntry { + DomCode dom_code; + KeyboardCode key_code; +} kDomCodeToKeyboardCodeMap[] = { + // Entries are ordered by numeric value of the DomCode enum, + // which is the USB physical key code. + // DomCode::HYPER 0x000010 Hyper + // DomCode::SUPER 0x000011 Super + // DomCode::FN 0x000012 Fn + // DomCode::FN_LOCK 0x000013 FLock + // DomCode::SUSPEND 0x000014 Suspend + // DomCode::RESUME 0x000015 Resume + // DomCode::TURBO 0x000016 Turbo + {DomCode::SLEEP, VKEY_SLEEP}, // 0x010082 Sleep + // DomCode::WAKE_UP 0x010083 WakeUp + {DomCode::KEY_A, VKEY_A}, // 0x070004 KeyA + {DomCode::KEY_B, VKEY_B}, // 0x070005 KeyB + {DomCode::KEY_C, VKEY_C}, // 0x070006 KeyC + {DomCode::KEY_D, VKEY_D}, // 0x070007 KeyD + {DomCode::KEY_E, VKEY_E}, // 0x070008 KeyE + {DomCode::KEY_F, VKEY_F}, // 0x070009 KeyF + {DomCode::KEY_G, VKEY_G}, // 0x07000A KeyG + {DomCode::KEY_H, VKEY_H}, // 0x07000B KeyH + {DomCode::KEY_I, VKEY_I}, // 0x07000C KeyI + {DomCode::KEY_J, VKEY_J}, // 0x07000D KeyJ + {DomCode::KEY_K, VKEY_K}, // 0x07000E KeyK + {DomCode::KEY_L, VKEY_L}, // 0x07000F KeyL + {DomCode::KEY_M, VKEY_M}, // 0x070010 KeyM + {DomCode::KEY_N, VKEY_N}, // 0x070011 KeyN + {DomCode::KEY_O, VKEY_O}, // 0x070012 KeyO + {DomCode::KEY_P, VKEY_P}, // 0x070013 KeyP + {DomCode::KEY_Q, VKEY_Q}, // 0x070014 KeyQ + {DomCode::KEY_R, VKEY_R}, // 0x070015 KeyR + {DomCode::KEY_S, VKEY_S}, // 0x070016 KeyS + {DomCode::KEY_T, VKEY_T}, // 0x070017 KeyT + {DomCode::KEY_U, VKEY_U}, // 0x070018 KeyU + {DomCode::KEY_V, VKEY_V}, // 0x070019 KeyV + {DomCode::KEY_W, VKEY_W}, // 0x07001A KeyW + {DomCode::KEY_X, VKEY_X}, // 0x07001B KeyX + {DomCode::KEY_Y, VKEY_Y}, // 0x07001C KeyY + {DomCode::KEY_Z, VKEY_Z}, // 0x07001D KeyZ + {DomCode::DIGIT1, VKEY_1}, // 0x07001E Digit1 + {DomCode::DIGIT2, VKEY_2}, // 0x07001F Digit2 + {DomCode::DIGIT3, VKEY_3}, // 0x070020 Digit3 + {DomCode::DIGIT4, VKEY_4}, // 0x070021 Digit4 + {DomCode::DIGIT5, VKEY_5}, // 0x070022 Digit5 + {DomCode::DIGIT6, VKEY_6}, // 0x070023 Digit6 + {DomCode::DIGIT7, VKEY_7}, // 0x070024 Digit7 + {DomCode::DIGIT8, VKEY_8}, // 0x070025 Digit8 + {DomCode::DIGIT9, VKEY_9}, // 0x070026 Digit9 + {DomCode::DIGIT0, VKEY_0}, // 0x070027 Digit0 + {DomCode::ENTER, VKEY_RETURN}, // 0x070028 Enter + {DomCode::ESCAPE, VKEY_ESCAPE}, // 0x070029 Escape + {DomCode::BACKSPACE, VKEY_BACK}, // 0x07002A Backspace + {DomCode::TAB, VKEY_TAB}, // 0x07002B Tab + {DomCode::SPACE, VKEY_SPACE}, // 0x07002C Space + {DomCode::MINUS, VKEY_OEM_MINUS}, // 0x07002D Minus + {DomCode::EQUAL, VKEY_OEM_PLUS}, // 0x07002E Equal + {DomCode::BRACKET_LEFT, VKEY_OEM_4}, // 0x07002F BracketLeft + {DomCode::BRACKET_RIGHT, VKEY_OEM_6}, // 0x070030 BracketRight + {DomCode::BACKSLASH, VKEY_OEM_5}, // 0x070031 Backslash + // DomCode::INTL_HASH, VKEY_OEM_5 // 0x070032 IntlHash + {DomCode::SEMICOLON, VKEY_OEM_1}, // 0x070033 Semicolon + {DomCode::QUOTE, VKEY_OEM_7}, // 0x070034 Quote + {DomCode::BACKQUOTE, VKEY_OEM_3}, // 0x070035 Backquote + {DomCode::COMMA, VKEY_OEM_COMMA}, // 0x070036 Comma + {DomCode::PERIOD, VKEY_OEM_PERIOD}, // 0x070037 Period + {DomCode::SLASH, VKEY_OEM_2}, // 0x070038 Slash + {DomCode::CAPS_LOCK, VKEY_CAPITAL}, // 0x070039 CapsLock + {DomCode::F1, VKEY_F1}, // 0x07003A F1 + {DomCode::F2, VKEY_F2}, // 0x07003B F2 + {DomCode::F3, VKEY_F3}, // 0x07003C F3 + {DomCode::F4, VKEY_F4}, // 0x07003D F4 + {DomCode::F5, VKEY_F5}, // 0x07003E F5 + {DomCode::F6, VKEY_F6}, // 0x07003F F6 + {DomCode::F7, VKEY_F7}, // 0x070040 F7 + {DomCode::F8, VKEY_F8}, // 0x070041 F8 + {DomCode::F9, VKEY_F9}, // 0x070042 F9 + {DomCode::F10, VKEY_F10}, // 0x070043 F10 + {DomCode::F11, VKEY_F11}, // 0x070044 F11 + {DomCode::F12, VKEY_F12}, // 0x070045 F12 + {DomCode::PRINT_SCREEN, VKEY_SNAPSHOT}, // 0x070046 PrintScreen + {DomCode::SCROLL_LOCK, VKEY_SCROLL}, // 0x070047 ScrollLock + {DomCode::PAUSE, VKEY_PAUSE}, // 0x070048 Pause + {DomCode::INSERT, VKEY_INSERT}, // 0x070049 Insert + {DomCode::HOME, VKEY_HOME}, // 0x07004A Home + {DomCode::PAGE_UP, VKEY_PRIOR}, // 0x07004B PageUp + {DomCode::DEL, VKEY_DELETE}, // 0x07004C Delete + {DomCode::END, VKEY_END}, // 0x07004D End + {DomCode::PAGE_DOWN, VKEY_NEXT}, // 0x07004E PageDown + {DomCode::ARROW_RIGHT, VKEY_RIGHT}, // 0x07004F ArrowRight + {DomCode::ARROW_LEFT, VKEY_LEFT}, // 0x070050 ArrowLeft + {DomCode::ARROW_DOWN, VKEY_DOWN}, // 0x070051 ArrowDown + {DomCode::ARROW_UP, VKEY_UP}, // 0x070052 ArrowUp + {DomCode::NUM_LOCK, VKEY_NUMLOCK}, // 0x070053 NumLock + {DomCode::NUMPAD_DIVIDE, VKEY_DIVIDE}, // 0x070054 NumpadDivide + {DomCode::NUMPAD_MULTIPLY, VKEY_MULTIPLY}, // 0x070055 NumpadMultiply + {DomCode::NUMPAD_SUBTRACT, VKEY_SUBTRACT}, // 0x070056 NumpadSubtract + {DomCode::NUMPAD_ADD, VKEY_ADD}, // 0x070057 NumpadAdd + {DomCode::NUMPAD_ENTER, VKEY_RETURN}, // 0x070058 NumpadEnter + {DomCode::NUMPAD1, VKEY_NUMPAD1}, // 0x070059 Numpad1 + {DomCode::NUMPAD2, VKEY_NUMPAD2}, // 0x07005A Numpad2 + {DomCode::NUMPAD3, VKEY_NUMPAD3}, // 0x07005B Numpad3 + {DomCode::NUMPAD4, VKEY_NUMPAD4}, // 0x07005C Numpad4 + {DomCode::NUMPAD5, VKEY_NUMPAD5}, // 0x07005D Numpad5 + {DomCode::NUMPAD6, VKEY_NUMPAD6}, // 0x07005E Numpad6 + {DomCode::NUMPAD7, VKEY_NUMPAD7}, // 0x07005F Numpad7 + {DomCode::NUMPAD8, VKEY_NUMPAD8}, // 0x070060 Numpad8 + {DomCode::NUMPAD9, VKEY_NUMPAD9}, // 0x070061 Numpad9 + {DomCode::NUMPAD0, VKEY_NUMPAD0}, // 0x070062 Numpad0 + {DomCode::NUMPAD_DECIMAL, VKEY_DECIMAL}, // 0x070063 NumpadDecimal + {DomCode::INTL_BACKSLASH, VKEY_OEM_102}, // 0x070064 IntlBackslash + {DomCode::CONTEXT_MENU, VKEY_APPS}, // 0x070065 ContextMenu +#if defined(OS_POSIX) + {DomCode::POWER, VKEY_POWER}, // 0x070066 Power +#endif + // DomCode::NUMPAD_EQUAL 0x070067 NumpadEqual + {DomCode::F13, VKEY_F13}, // 0x070068 F13 + {DomCode::F14, VKEY_F14}, // 0x070069 F14 + {DomCode::F15, VKEY_F15}, // 0x07006A F15 + {DomCode::F16, VKEY_F16}, // 0x07006B F16 + {DomCode::F17, VKEY_F17}, // 0x07006C F17 + {DomCode::F18, VKEY_F18}, // 0x07006D F18 + {DomCode::F19, VKEY_F19}, // 0x07006E F19 + {DomCode::F20, VKEY_F20}, // 0x07006F F20 + {DomCode::F21, VKEY_F21}, // 0x070070 F21 + {DomCode::F22, VKEY_F22}, // 0x070071 F22 + {DomCode::F23, VKEY_F23}, // 0x070072 F23 + {DomCode::F24, VKEY_F24}, // 0x070073 F24 + {DomCode::OPEN, VKEY_EXECUTE}, // 0x070074 Open + {DomCode::HELP, VKEY_HELP}, // 0x070075 Help + {DomCode::SELECT, VKEY_SELECT}, // 0x070077 Select + // DomCode::AGAIN 0x070079 Again + // DomCode::UNDO 0x07007A Undo + // DomCode::CUT 0x07007B Cut + // DomCode::COPY 0x07007C Copy + // DomCode::PASTE 0x07007D Paste + // DomCode::FIND 0x07007E Find + {DomCode::VOLUME_MUTE, VKEY_VOLUME_MUTE}, // 0x07007F VolumeMute + {DomCode::VOLUME_UP, VKEY_VOLUME_UP}, // 0x070080 VolumeUp + {DomCode::VOLUME_DOWN, VKEY_VOLUME_DOWN}, // 0x070081 VolumeDown + {DomCode::NUMPAD_COMMA, VKEY_OEM_COMMA}, // 0x070085 NumpadComma + {DomCode::INTL_RO, VKEY_OEM_102}, // 0x070087 IntlRo + {DomCode::KANA_MODE, VKEY_KANA}, // 0x070088 KanaMode + {DomCode::INTL_YEN, VKEY_OEM_5}, // 0x070089 IntlYen + {DomCode::CONVERT, VKEY_CONVERT}, // 0x07008A Convert + {DomCode::NON_CONVERT, VKEY_NONCONVERT}, // 0x07008B NonConvert + {DomCode::LANG1, VKEY_KANA}, // 0x070090 Lang1 + {DomCode::LANG2, VKEY_KANJI}, // 0x070091 Lang2 + // DomCode::LANG3 0x070092 Lang3 + // DomCode::LANG4 0x070093 Lang4 + // DomCode::LANG5 0x070094 Lang5 + // DomCode::ABORT 0x07009B Abort + // DomCode::PROPS 0x0700A3 Props + // DomCode::NUMPAD_PAREN_LEFT 0x0700B6 NumpadParenLeft + // DomCode::NUMPAD_PAREN_RIGHT 0x0700B7 NumpadParenRight + {DomCode::NUMPAD_BACKSPACE, VKEY_BACK}, // 0x0700BB NumpadBackspace + // DomCode::NUMPAD_MEMORY_STORE 0x0700D0 NumpadMemoryStore + // DomCode::NUMPAD_MEMORY_RECALL 0x0700D1 NumpadMemoryRecall + // DomCode::NUMPAD_MEMORY_CLEAR 0x0700D2 NumpadMemoryClear + // DomCode::NUMPAD_MEMORY_ADD 0x0700D3 NumpadMemoryAdd + // DomCode::NUMPAD_MEMORY_SUBTRACT 0x0700D4 NumpadMemorySubtract + {DomCode::NUMPAD_CLEAR, VKEY_CLEAR}, // 0x0700D8 NumpadClear + {DomCode::NUMPAD_CLEAR_ENTRY, VKEY_CLEAR}, // 0x0700D9 NumpadClearEntry + {DomCode::CONTROL_LEFT, VKEY_LCONTROL}, // 0x0700E0 ControlLeft + {DomCode::SHIFT_LEFT, VKEY_LSHIFT}, // 0x0700E1 ShiftLeft + {DomCode::ALT_LEFT, VKEY_LMENU}, // 0x0700E2 AltLeft + {DomCode::OS_LEFT, VKEY_LWIN}, // 0x0700E3 OSLeft + {DomCode::CONTROL_RIGHT, VKEY_RCONTROL}, // 0x0700E4 ControlRight + {DomCode::SHIFT_RIGHT, VKEY_RSHIFT}, // 0x0700E5 ShiftRight + {DomCode::ALT_RIGHT, VKEY_RMENU}, // 0x0700E6 AltRight + {DomCode::OS_RIGHT, VKEY_RWIN}, // 0x0700E7 OSRight +#if defined(OS_POSIX) + {DomCode::BRIGHTNESS_UP, + VKEY_BRIGHTNESS_UP}, // 0x0C006F BrightnessUp + {DomCode::BRIGHTNESS_DOWN, + VKEY_BRIGHTNESS_DOWN}, // 0x0C0070 BrightnessDown +#endif + {DomCode::MEDIA_TRACK_NEXT, + VKEY_MEDIA_NEXT_TRACK}, // 0x0C00B5 MediaTrackNext + {DomCode::MEDIA_TRACK_PREVIOUS, + VKEY_MEDIA_PREV_TRACK}, // 0x0C00B6 MediaTrackPrevious + {DomCode::MEDIA_STOP, VKEY_MEDIA_STOP}, // 0x0C00B7 MediaStop + // DomCode::EJECT 0x0C00B8 Eject + {DomCode::MEDIA_PLAY_PAUSE, + VKEY_MEDIA_PLAY_PAUSE}, // 0x0C00CD MediaPlayPause + {DomCode::MEDIA_SELECT, + VKEY_MEDIA_LAUNCH_MEDIA_SELECT}, // 0x0C0183 MediaSelect + {DomCode::LAUNCH_MAIL, + VKEY_MEDIA_LAUNCH_MAIL}, // 0x0C018A LaunchMail + {DomCode::LAUNCH_APP2, + VKEY_MEDIA_LAUNCH_APP2}, // 0x0C0192 LaunchApp2 + {DomCode::LAUNCH_APP1, + VKEY_MEDIA_LAUNCH_APP1}, // 0x0C0194 LaunchApp1 + {DomCode::BROWSER_SEARCH, + VKEY_BROWSER_SEARCH}, // 0x0C0221 BrowserSearch + {DomCode::BROWSER_HOME, VKEY_BROWSER_HOME}, // 0x0C0223 BrowserHome + {DomCode::BROWSER_BACK, VKEY_BROWSER_BACK}, // 0x0C0224 BrowserBack + {DomCode::BROWSER_FORWARD, + VKEY_BROWSER_FORWARD}, // 0x0C0225 BrowserForward + {DomCode::BROWSER_STOP, VKEY_BROWSER_STOP}, // 0x0C0226 BrowserStop + {DomCode::BROWSER_REFRESH, + VKEY_BROWSER_REFRESH}, // 0x0C0227 BrowserRefresh + {DomCode::BROWSER_FAVORITES, + VKEY_BROWSER_FAVORITES}, // 0x0C022A BrowserFavorites + {DomCode::ZOOM_TOGGLE, VKEY_ZOOM}, // 0x0C0232 ZoomToggle +}; + +// This table, used by UsLayoutKeyboardCodeToDomCode(), maps legacy +// Windows-based VKEY values that are not part of kDomCodeToKeyboardCodeMap[] +// to suitable DomCode values, where practical. +const DomCodeToKeyboardCodeEntry kFallbackKeyboardCodeToDomCodeMap[] = { + {DomCode::ALT_LEFT, VKEY_MENU}, + {DomCode::ALT_RIGHT, VKEY_ALTGR}, + {DomCode::BACKQUOTE, VKEY_DBE_SBCSCHAR}, +#if defined(OS_POSIX) + {DomCode::CONTEXT_MENU, VKEY_COMPOSE}, +#endif + {DomCode::CONTROL_LEFT, VKEY_CONTROL}, + {DomCode::LANG1, VKEY_HANGUL}, + {DomCode::LANG2, VKEY_HANJA}, + {DomCode::LANG5, VKEY_DBE_DBCSCHAR}, + {DomCode::NUMPAD_CLEAR, VKEY_OEM_CLEAR}, + {DomCode::PROPS, VKEY_CRSEL}, + {DomCode::SHIFT_LEFT, VKEY_SHIFT}, + {DomCode::SUPER, VKEY_OEM_8}, + // + // VKEYs with no existing corresponding DomCode, but a USB usage code: + // {DomCode::SYS_REQ, VKEY_ATTN}, // 0x07009A SysReq + // {DomCode::SEPARATOR, VKEY_SEPARATOR}, // 0x07009F Separator + // {DomCode::EX_SEL, VKEY_EXSEL}, // 0x0700A4 ExSel + // {DomCode::PRINT, VKEY_PRINT}, // 0x0C0208 AC Print + // {DomCode::MEDIA_PLAY, VKEY_PLAY}, // 0x0C00B0 MediaPlay + // {DomCode::MEDIA_REWIND, VKEY_OEM_103}, // 0x0C00B4 MediaRewind + // {DomCode::MEDIA_FAST_FORWARD, VKEY_OEM_104}, + // // 0x0C00B3 MediaFastForward + // + // VKEYs with no corresponding DomCode and no obvious USB usage code: + // VKEY_ACCEPT + // VKEY_BACKTAB + // VKEY_EREOF + // VKEY_FINAL + // VKEY_JUNJA + // VKEY_KBD_BRIGHTNESS_DOWN + // VKEY_KBD_BRIGHTNESS_UP + // VKEY_MODECHANGE + // VKEY_NONAME + // VKEY_PA1 + // VKEY_PACKET + // VKEY_PROCESSKEY + // VKEY_WLAN +}; + +} // namespace ui + +#endif // UI_EVENTS_KEYCODES_DOM_US_LAYOUT_DATA_H_
diff --git a/ui/events/keycodes/keyboard_code_conversion.cc b/ui/events/keycodes/keyboard_code_conversion.cc index b71dd6f0b..e31512b 100644 --- a/ui/events/keycodes/keyboard_code_conversion.cc +++ b/ui/events/keycodes/keyboard_code_conversion.cc
@@ -4,9 +4,12 @@ #include "ui/events/keycodes/keyboard_code_conversion.h" +#include <algorithm> + #include "ui/events/event_constants.h" #include "ui/events/keycodes/dom3/dom_code.h" #include "ui/events/keycodes/dom3/dom_key.h" +#include "ui/events/keycodes/dom_us_layout_data.h" namespace ui { @@ -130,6 +133,29 @@ (code == DomCode::ALT_RIGHT) || (code == DomCode::OS_RIGHT); } +bool IsModifierDomCode(DomCode code) { + return (code == DomCode::CONTROL_LEFT) || (code == DomCode::CONTROL_RIGHT) || + (code == DomCode::SHIFT_LEFT) || (code == DomCode::SHIFT_RIGHT) || + (code == DomCode::ALT_LEFT) || (code == DomCode::ALT_RIGHT) || + (code == DomCode::OS_LEFT) || (code == DomCode::OS_RIGHT); +} + +// Returns the Windows-based VKEY value corresponding to a DOM Level 3 |code|, +// assuming a base US English layout. The returned VKEY is located +// (e.g. VKEY_LSHIFT). +KeyboardCode DomCodeToUsLayoutKeyboardCode(DomCode dom_code) { + const DomCodeToKeyboardCodeEntry* end = + kDomCodeToKeyboardCodeMap + arraysize(kDomCodeToKeyboardCodeMap); + const DomCodeToKeyboardCodeEntry* found = + std::lower_bound(kDomCodeToKeyboardCodeMap, end, dom_code, + [](const DomCodeToKeyboardCodeEntry& a, DomCode b) { + return static_cast<int>(a.dom_code) < static_cast<int>(b); + }); + if ((found != end) && (found->dom_code == dom_code)) + return found->key_code; + return VKEY_UNKNOWN; +} + } // anonymous namespace base::char16 GetCharacterFromKeyCode(KeyboardCode key_code, int flags) { @@ -267,6 +293,151 @@ return false; } +bool DomCodeToUsLayoutMeaning(DomCode dom_code, + int flags, + DomKey* out_dom_key, + base::char16* out_character, + KeyboardCode* out_key_code) { + if ((flags & EF_CONTROL_DOWN) == EF_CONTROL_DOWN) { + if (DomCodeToControlCharacter(dom_code, flags, out_dom_key, out_character, + out_key_code)) { + return true; + } + if (!IsModifierDomCode(dom_code)) { + *out_dom_key = DomKey::UNIDENTIFIED; + *out_character = 0; + *out_key_code = LocatedToNonLocatedKeyboardCode( + DomCodeToUsLayoutKeyboardCode(dom_code)); + return true; + } + } else { + for (const auto& it : kPrintableCodeMap) { + if (it.dom_code == dom_code) { + int state = ((flags & EF_SHIFT_DOWN) == EF_SHIFT_DOWN); + base::char16 ch = it.character[state]; + *out_dom_key = DomKey::CHARACTER; + *out_character = ch; + if ((flags & EF_CAPS_LOCK_DOWN) == EF_CAPS_LOCK_DOWN) { + ch |= 0x20; + if ((ch >= 'a') && (ch <= 'z')) + *out_character = it.character[state ^ 1]; + } + *out_key_code = LocatedToNonLocatedKeyboardCode( + DomCodeToUsLayoutKeyboardCode(dom_code)); + return true; + } + } + } + for (const auto& it : kNonPrintableCodeMap) { + if (it.dom_code == dom_code) { + *out_dom_key = it.dom_key; + *out_character = it.character; + *out_key_code = NonPrintableDomKeyToKeyboardCode(it.dom_key); + return true; + } + } + return false; +} + +bool DomCodeToControlCharacter(DomCode dom_code, + int flags, + DomKey* dom_key, + base::char16* character, + KeyboardCode* key_code) { + if ((flags & EF_CONTROL_DOWN) == 0) + return false; + + int code = static_cast<int>(dom_code); + const int kKeyA = static_cast<int>(DomCode::KEY_A); + // Control-A - Control-Z map to 0x01 - 0x1A. + if (code >= kKeyA && code <= static_cast<int>(DomCode::KEY_Z)) { + *character = static_cast<base::char16>(code - kKeyA + 1); + switch (dom_code) { + case DomCode::KEY_H: + *dom_key = DomKey::BACKSPACE; + *key_code = VKEY_BACK; + break; + case DomCode::KEY_I: + *dom_key = DomKey::TAB; + *key_code = VKEY_TAB; + break; + case DomCode::KEY_M: + *dom_key = DomKey::ENTER; + *key_code = VKEY_RETURN; + break; + default: + *dom_key = DomKey::CHARACTER; + *key_code = static_cast<KeyboardCode>(code - kKeyA + VKEY_A); + break; + } + return true; + } + + if (flags & EF_SHIFT_DOWN) { + switch (dom_code) { + case DomCode::DIGIT2: + // NUL + *character = 0; + *dom_key = DomKey::CHARACTER; + *key_code = VKEY_2; + return true; + case DomCode::DIGIT6: + // RS + *character = 0x1E; + *dom_key = DomKey::CHARACTER; + *key_code = VKEY_6; + return true; + case DomCode::MINUS: + // US + *character = 0x1F; + *dom_key = DomKey::CHARACTER; + *key_code = VKEY_OEM_MINUS; + return true; + default: + return false; + } + } + + switch (dom_code) { + case DomCode::ENTER: + // NL + *character = 0x0A; + *dom_key = DomKey::CHARACTER; + *key_code = VKEY_RETURN; + return true; + case DomCode::BRACKET_LEFT: + // ESC + *character = 0x1B; + *dom_key = DomKey::ESCAPE; + *key_code = VKEY_OEM_4; + return true; + case DomCode::BACKSLASH: + // FS + *character = 0x1C; + *dom_key = DomKey::CHARACTER; + *key_code = VKEY_OEM_5; + return true; + case DomCode::BRACKET_RIGHT: + // GS + *character = 0x1D; + *dom_key = DomKey::CHARACTER; + *key_code = VKEY_OEM_6; + return true; + default: + return false; + } +} + +// Returns a Windows-based VKEY for a non-printable DOM Level 3 |key|. +// The returned VKEY is non-positional (e.g. VKEY_SHIFT). +KeyboardCode NonPrintableDomKeyToKeyboardCode(DomKey dom_key) { + for (const auto& it : kDomKeyToKeyboardCodeMap) { + if (it.dom_key == dom_key) + return it.key_code; + } + return VKEY_UNKNOWN; +} + // Determine the non-located VKEY corresponding to a located VKEY. KeyboardCode LocatedToNonLocatedKeyboardCode(KeyboardCode key_code) { switch (key_code) { @@ -343,4 +514,17 @@ } } +DomCode UsLayoutKeyboardCodeToDomCode(KeyboardCode key_code) { + key_code = NonLocatedToLocatedKeyboardCode(key_code, DomCode::NONE); + for (const auto& it : kDomCodeToKeyboardCodeMap) { + if (it.key_code == key_code) + return it.dom_code; + } + for (const auto& it : kFallbackKeyboardCodeToDomCodeMap) { + if (it.key_code == key_code) + return it.dom_code; + } + return DomCode::NONE; +} + } // namespace ui
diff --git a/ui/events/keycodes/keyboard_code_conversion.h b/ui/events/keycodes/keyboard_code_conversion.h index 0c14ed74..8b92ba1 100644 --- a/ui/events/keycodes/keyboard_code_conversion.h +++ b/ui/events/keycodes/keyboard_code_conversion.h
@@ -5,6 +5,7 @@ #ifndef UI_EVENTS_KEYCODES_KEYBOARD_CODE_CONVERSION_H_ #define UI_EVENTS_KEYCODES_KEYBOARD_CODE_CONVERSION_H_ +#include "base/compiler_specific.h" #include "base/strings/string16.h" #include "ui/events/events_base_export.h" #include "ui/events/keycodes/keyboard_codes.h" @@ -18,22 +19,20 @@ // platform independent way. It supports control characters as well. // It assumes a US keyboard layout is used, so it may only be used when there // is no native event or no better way to get the character. +// // For example, if a virtual keyboard implementation can only generate key // events with key_code and flags information, then there is no way for us to // determine the actual character that should be generate by the key. Because // a key_code only represents a physical key on the keyboard, it has nothing // to do with the actual character printed on that key. In such case, the only // thing we can do is to assume that we are using a US keyboard and get the -// character according to US keyboard layout definition. -// If a virtual keyboard implementation wants to support other keyboard -// layouts, that may generate different text for a certain key than on a US -// keyboard, a special native event object should be introduced to carry extra -// information to help determine the correct character. -// Take XKeyEvent as an example, it contains not only keycode and modifier -// flags but also group and other extra XKB information to help determine the -// correct character. That's why we can use XLookupString() function to get -// the correct text generated by a X key event (See how is GetCharacter() -// implemented in event_x.cc). +// character according to US keyboard layout definition. Preferably, such +// events should be created using a full KeyEvent constructor, explicitly +// specifying the character and DOM 3 values as well as the legacy VKEY. +// +// TODO(kpschoedel): replace remaining uses of the ...FromKeyCode() functions +// and remove them, to avoid future creation of underspecified key events. +// crbug.com/444045 EVENTS_BASE_EXPORT base::char16 GetCharacterFromKeyCode(KeyboardCode key_code, int flags); EVENTS_BASE_EXPORT bool GetMeaningFromKeyCode(KeyboardCode key_code, @@ -41,6 +40,41 @@ DomKey* dom_key, base::char16* character); +// Helper function to map a physical key state (dom_code and flags) +// to a meaning (dom_key and character, together corresponding to the +// DOM keyboard event |key| value), along with a corresponding Windows-based +// key_code. +// +// This follows a US keyboard layout, so it should only be used when there +// is no other better way to obtain the meaning (e.g. actual keyboard layout). +// Returns true and sets the output parameters if the (dom_code, flags) pair +// has an interpretation in the US English layout; otherwise the output +// parameters are untouched. +EVENTS_BASE_EXPORT bool DomCodeToUsLayoutMeaning(DomCode dom_code, + int flags, + DomKey* dom_key, + base::char16* character, + KeyboardCode* key_code) + WARN_UNUSED_RESULT; + +// Obtains the control character corresponding to a physical key; +// that is, the meaning of the physical key state (dom_code, and flags +// containing EF_CONTROL_DOWN) under the base US English layout. +// Returns true and sets the output parameters if the (dom_code, flags) pair +// is interpreted as a control character; otherwise the output parameters +// are untouched. +EVENTS_BASE_EXPORT bool DomCodeToControlCharacter(DomCode dom_code, + int flags, + DomKey* dom_key, + base::char16* character, + KeyboardCode* key_code) + WARN_UNUSED_RESULT; + +// Returns a Windows-based VKEY for a non-printable DOM Level 3 |key|. +// The returned VKEY is non-located (e.g. VKEY_SHIFT). +EVENTS_BASE_EXPORT KeyboardCode +NonPrintableDomKeyToKeyboardCode(DomKey dom_key); + // Determine the non-located VKEY corresponding to a located VKEY. // Most modifier keys have two kinds of KeyboardCode: located (e.g. // VKEY_LSHIFT and VKEY_RSHIFT), that indentify one of two specific @@ -53,6 +87,12 @@ EVENTS_BASE_EXPORT KeyboardCode NonLocatedToLocatedKeyboardCode(KeyboardCode key_code, DomCode dom_code); +// Returns a DOM Level 3 |code| from a Windows-based VKEY value. +// This assumes a US layout and should only be used when |code| cannot be +// determined from a physical scan code, for example when a key event was +// generated synthetically by JavaScript with only a VKEY value supplied. +EVENTS_BASE_EXPORT DomCode UsLayoutKeyboardCodeToDomCode(KeyboardCode key_code); + } // namespace ui #endif // UI_EVENTS_KEYCODES_KEYBOARD_CODE_CONVERSION_H_
diff --git a/ui/events/keycodes/keyboard_code_conversion_unittest.cc b/ui/events/keycodes/keyboard_code_conversion_unittest.cc new file mode 100644 index 0000000..e22208a --- /dev/null +++ b/ui/events/keycodes/keyboard_code_conversion_unittest.cc
@@ -0,0 +1,523 @@ +// 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/events/keycodes/keyboard_code_conversion.h" + +#include "base/basictypes.h" +#include "base/strings/stringprintf.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/events/event_constants.h" +#include "ui/events/keycodes/dom3/dom_code.h" +#include "ui/events/keycodes/dom3/dom_key.h" +#include "ui/events/keycodes/dom4/keycode_converter.h" +#include "ui/events/keycodes/keyboard_codes.h" + +namespace { + +struct Meaning { + bool defined; + ui::DomKey dom_key; + base::char16 character; + ui::KeyboardCode legacy_key_code; +}; + +const Meaning kUndefined = {false, ui::DomKey::NONE, 0, ui::VKEY_UNKNOWN}; + +void CheckDomCodeToMeaning(const char* label, + bool f(ui::DomCode dom_code, + int flags, + ui::DomKey* out_dom_key, + base::char16* out_character, + ui::KeyboardCode* out_key_code), + ui::DomCode dom_code, + int event_flags, + const Meaning& result) { + ui::DomKey result_dom_key = ui::DomKey::NONE; + base::char16 result_character = 0; + ui::KeyboardCode result_legacy_key_code = ui::VKEY_UNKNOWN; + bool success = f(dom_code, event_flags, &result_dom_key, &result_character, + &result_legacy_key_code); + SCOPED_TRACE( + base::StringPrintf("%s %s %06X:%04X", label, + ui::KeycodeConverter::DomCodeToCodeString(dom_code), + static_cast<int>(dom_code), event_flags)); + EXPECT_EQ(result.defined, success); + if (success) { + EXPECT_EQ(result.dom_key, result_dom_key); + EXPECT_EQ(result.character, result_character); + EXPECT_EQ(result.legacy_key_code, result_legacy_key_code); + } else { + // Should not have touched output parameters. + EXPECT_EQ(ui::DomKey::NONE, result_dom_key); + EXPECT_EQ(0, result_character); + EXPECT_EQ(ui::VKEY_UNKNOWN, result_legacy_key_code); + } +} + +TEST(KeyboardCodeConversion, ControlCharacters) { + // The codes in this table are handled by |DomCodeToControlCharacter()|. + static const struct { + ui::DomCode dom_code; + Meaning control; + Meaning control_shift; + } kControlCharacters[] = { + {ui::DomCode::KEY_A, + {true, ui::DomKey::CHARACTER, 0x01, ui::VKEY_A}, + {true, ui::DomKey::CHARACTER, 0x01, ui::VKEY_A}}, + {ui::DomCode::KEY_B, + {true, ui::DomKey::CHARACTER, 0x02, ui::VKEY_B}, + {true, ui::DomKey::CHARACTER, 0x02, ui::VKEY_B}}, + {ui::DomCode::KEY_C, + {true, ui::DomKey::CHARACTER, 0x03, ui::VKEY_C}, + {true, ui::DomKey::CHARACTER, 0x03, ui::VKEY_C}}, + {ui::DomCode::KEY_D, + {true, ui::DomKey::CHARACTER, 0x04, ui::VKEY_D}, + {true, ui::DomKey::CHARACTER, 0x04, ui::VKEY_D}}, + {ui::DomCode::KEY_E, + {true, ui::DomKey::CHARACTER, 0x05, ui::VKEY_E}, + {true, ui::DomKey::CHARACTER, 0x05, ui::VKEY_E}}, + {ui::DomCode::KEY_F, + {true, ui::DomKey::CHARACTER, 0x06, ui::VKEY_F}, + {true, ui::DomKey::CHARACTER, 0x06, ui::VKEY_F}}, + {ui::DomCode::KEY_G, + {true, ui::DomKey::CHARACTER, 0x07, ui::VKEY_G}, + {true, ui::DomKey::CHARACTER, 0x07, ui::VKEY_G}}, + {ui::DomCode::KEY_H, + {true, ui::DomKey::BACKSPACE, 0x08, ui::VKEY_BACK}, + {true, ui::DomKey::BACKSPACE, 0x08, ui::VKEY_BACK}}, + {ui::DomCode::KEY_I, + {true, ui::DomKey::TAB, 0x09, ui::VKEY_TAB}, + {true, ui::DomKey::TAB, 0x09, ui::VKEY_TAB}}, + {ui::DomCode::KEY_J, + {true, ui::DomKey::CHARACTER, 0x0A, ui::VKEY_J}, + {true, ui::DomKey::CHARACTER, 0x0A, ui::VKEY_J}}, + {ui::DomCode::KEY_K, + {true, ui::DomKey::CHARACTER, 0x0B, ui::VKEY_K}, + {true, ui::DomKey::CHARACTER, 0x0B, ui::VKEY_K}}, + {ui::DomCode::KEY_L, + {true, ui::DomKey::CHARACTER, 0x0C, ui::VKEY_L}, + {true, ui::DomKey::CHARACTER, 0x0C, ui::VKEY_L}}, + {ui::DomCode::KEY_M, + {true, ui::DomKey::ENTER, 0x0D, ui::VKEY_RETURN}, + {true, ui::DomKey::ENTER, 0x0D, ui::VKEY_RETURN}}, + {ui::DomCode::KEY_N, + {true, ui::DomKey::CHARACTER, 0x0E, ui::VKEY_N}, + {true, ui::DomKey::CHARACTER, 0x0E, ui::VKEY_N}}, + {ui::DomCode::KEY_O, + {true, ui::DomKey::CHARACTER, 0x0F, ui::VKEY_O}, + {true, ui::DomKey::CHARACTER, 0x0F, ui::VKEY_O}}, + {ui::DomCode::KEY_P, + {true, ui::DomKey::CHARACTER, 0x10, ui::VKEY_P}, + {true, ui::DomKey::CHARACTER, 0x10, ui::VKEY_P}}, + {ui::DomCode::KEY_Q, + {true, ui::DomKey::CHARACTER, 0x11, ui::VKEY_Q}, + {true, ui::DomKey::CHARACTER, 0x11, ui::VKEY_Q}}, + {ui::DomCode::KEY_R, + {true, ui::DomKey::CHARACTER, 0x12, ui::VKEY_R}, + {true, ui::DomKey::CHARACTER, 0x12, ui::VKEY_R}}, + {ui::DomCode::KEY_S, + {true, ui::DomKey::CHARACTER, 0x13, ui::VKEY_S}, + {true, ui::DomKey::CHARACTER, 0x13, ui::VKEY_S}}, + {ui::DomCode::KEY_T, + {true, ui::DomKey::CHARACTER, 0x14, ui::VKEY_T}, + {true, ui::DomKey::CHARACTER, 0x14, ui::VKEY_T}}, + {ui::DomCode::KEY_U, + {true, ui::DomKey::CHARACTER, 0x15, ui::VKEY_U}, + {true, ui::DomKey::CHARACTER, 0x15, ui::VKEY_U}}, + {ui::DomCode::KEY_V, + {true, ui::DomKey::CHARACTER, 0x16, ui::VKEY_V}, + {true, ui::DomKey::CHARACTER, 0x16, ui::VKEY_V}}, + {ui::DomCode::KEY_W, + {true, ui::DomKey::CHARACTER, 0x17, ui::VKEY_W}, + {true, ui::DomKey::CHARACTER, 0x17, ui::VKEY_W}}, + {ui::DomCode::KEY_X, + {true, ui::DomKey::CHARACTER, 0x18, ui::VKEY_X}, + {true, ui::DomKey::CHARACTER, 0x18, ui::VKEY_X}}, + {ui::DomCode::KEY_Y, + {true, ui::DomKey::CHARACTER, 0x19, ui::VKEY_Y}, + {true, ui::DomKey::CHARACTER, 0x19, ui::VKEY_Y}}, + {ui::DomCode::KEY_Z, + {true, ui::DomKey::CHARACTER, 0x1A, ui::VKEY_Z}, + {true, ui::DomKey::CHARACTER, 0x1A, ui::VKEY_Z}}, + }; + for (const auto& it : kControlCharacters) { + // Verify |DomCodeToControlCharacter()|. + CheckDomCodeToMeaning("c_cc_n", ui::DomCodeToControlCharacter, it.dom_code, + ui::EF_NONE, kUndefined); + CheckDomCodeToMeaning("c_cc_c", ui::DomCodeToControlCharacter, it.dom_code, + ui::EF_CONTROL_DOWN, it.control); + CheckDomCodeToMeaning("c_cc_cs", ui::DomCodeToControlCharacter, it.dom_code, + ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN, + it.control_shift); + // Verify |DomCodeToUsLayoutMeaning()|. + CheckDomCodeToMeaning("c_us_c", ui::DomCodeToUsLayoutMeaning, it.dom_code, + ui::EF_CONTROL_DOWN, it.control); + CheckDomCodeToMeaning("c_us_cs", ui::DomCodeToUsLayoutMeaning, it.dom_code, + ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN, + it.control_shift); + } + + // The codes in this table are sensitive to the Shift state, so they are + // handled differently by |DomCodeToControlCharacter()|, which returns false + // for unknown combinations, vs |DomCodeToUsLayoutMeaning()|, which returns + // true with DomKey::UNIDENTIFIED. + static const struct { + ui::DomCode dom_code; + Meaning cc_control; + Meaning cc_control_shift; + Meaning us_control; + Meaning us_control_shift; + } kShiftControlCharacters[] = { + {ui::DomCode::DIGIT2, + {false, ui::DomKey::NONE, 0, ui::VKEY_UNKNOWN}, + {true, ui::DomKey::CHARACTER, 0, ui::VKEY_2}, + {true, ui::DomKey::UNIDENTIFIED, 0, ui::VKEY_2}, + {true, ui::DomKey::CHARACTER, 0, ui::VKEY_2}}, + {ui::DomCode::DIGIT6, + {false, ui::DomKey::NONE, 0, ui::VKEY_UNKNOWN}, + {true, ui::DomKey::CHARACTER, 0x1E, ui::VKEY_6}, + {true, ui::DomKey::UNIDENTIFIED, 0, ui::VKEY_6}, + {true, ui::DomKey::CHARACTER, 0x1E, ui::VKEY_6}}, + {ui::DomCode::MINUS, + {false, ui::DomKey::NONE, 0, ui::VKEY_UNKNOWN}, + {true, ui::DomKey::CHARACTER, 0x1F, ui::VKEY_OEM_MINUS}, + {true, ui::DomKey::UNIDENTIFIED, 0, ui::VKEY_OEM_MINUS}, + {true, ui::DomKey::CHARACTER, 0x1F, ui::VKEY_OEM_MINUS}}, + {ui::DomCode::ENTER, + {true, ui::DomKey::CHARACTER, 0x0A, ui::VKEY_RETURN}, + {false, ui::DomKey::NONE, 0, ui::VKEY_UNKNOWN}, + {true, ui::DomKey::CHARACTER, 0x0A, ui::VKEY_RETURN}, + {true, ui::DomKey::UNIDENTIFIED, 0, ui::VKEY_RETURN}}, + {ui::DomCode::BRACKET_LEFT, + {true, ui::DomKey::ESCAPE, 0x1B, ui::VKEY_OEM_4}, + {false, ui::DomKey::NONE, 0, ui::VKEY_UNKNOWN}, + {true, ui::DomKey::ESCAPE, 0x1B, ui::VKEY_OEM_4}, + {true, ui::DomKey::UNIDENTIFIED, 0, ui::VKEY_OEM_4}}, + {ui::DomCode::BACKSLASH, + {true, ui::DomKey::CHARACTER, 0x1C, ui::VKEY_OEM_5}, + {false, ui::DomKey::NONE, 0, ui::VKEY_UNKNOWN}, + {true, ui::DomKey::CHARACTER, 0x1C, ui::VKEY_OEM_5}, + {true, ui::DomKey::UNIDENTIFIED, 0, ui::VKEY_OEM_5}}, + {ui::DomCode::BRACKET_RIGHT, + {true, ui::DomKey::CHARACTER, 0x1D, ui::VKEY_OEM_6}, + {false, ui::DomKey::NONE, 0, ui::VKEY_UNKNOWN}, + {true, ui::DomKey::CHARACTER, 0x1D, ui::VKEY_OEM_6}, + {true, ui::DomKey::UNIDENTIFIED, 0, ui::VKEY_OEM_6}}, + }; + for (const auto& it : kShiftControlCharacters) { + // Verify |DomCodeToControlCharacter()|. + CheckDomCodeToMeaning("s_cc_n", ui::DomCodeToControlCharacter, it.dom_code, + ui::EF_NONE, kUndefined); + CheckDomCodeToMeaning("s_cc_c", ui::DomCodeToControlCharacter, it.dom_code, + ui::EF_CONTROL_DOWN, it.cc_control); + CheckDomCodeToMeaning("s_cc_cs", ui::DomCodeToControlCharacter, it.dom_code, + ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN, + it.cc_control_shift); + // Verify |DomCodeToUsLayoutMeaning()|. + CheckDomCodeToMeaning("s_us_c", ui::DomCodeToUsLayoutMeaning, it.dom_code, + ui::EF_CONTROL_DOWN, it.us_control); + CheckDomCodeToMeaning("s_us_cs", ui::DomCodeToUsLayoutMeaning, it.dom_code, + ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN, + it.us_control_shift); + } + + // These codes are not handled by |DomCodeToControlCharacter()| directly. + static const struct { + ui::DomCode dom_code; + Meaning normal; + Meaning control; + } kNonControlCharacters[] = { + // Modifiers are handled by |DomCodeToUsLayoutMeaning()| without regard + // to whether Control is down. + {ui::DomCode::CONTROL_LEFT, + {true, ui::DomKey::CONTROL, 0, ui::VKEY_CONTROL}, + {true, ui::DomKey::CONTROL, 0, ui::VKEY_CONTROL}}, + {ui::DomCode::CONTROL_RIGHT, + {true, ui::DomKey::CONTROL, 0, ui::VKEY_CONTROL}, + {true, ui::DomKey::CONTROL, 0, ui::VKEY_CONTROL}}, + {ui::DomCode::SHIFT_LEFT, + {true, ui::DomKey::SHIFT, 0, ui::VKEY_SHIFT}, + {true, ui::DomKey::SHIFT, 0, ui::VKEY_SHIFT}}, + {ui::DomCode::SHIFT_RIGHT, + {true, ui::DomKey::SHIFT, 0, ui::VKEY_SHIFT}, + {true, ui::DomKey::SHIFT, 0, ui::VKEY_SHIFT}}, + {ui::DomCode::ALT_LEFT, + {true, ui::DomKey::ALT, 0, ui::VKEY_MENU}, + {true, ui::DomKey::ALT, 0, ui::VKEY_MENU}}, + {ui::DomCode::ALT_RIGHT, + {true, ui::DomKey::ALT, 0, ui::VKEY_MENU}, + {true, ui::DomKey::ALT, 0, ui::VKEY_MENU}}, + {ui::DomCode::OS_LEFT, + {true, ui::DomKey::OS, 0, ui::VKEY_LWIN}, + {true, ui::DomKey::OS, 0, ui::VKEY_LWIN}}, + {ui::DomCode::OS_RIGHT, + {true, ui::DomKey::OS, 0, ui::VKEY_LWIN}, + {true, ui::DomKey::OS, 0, ui::VKEY_LWIN}}, + // Non-modifiers (a representative sample here) succeed with + // DomKey::UNIDENTIFIED when Control is down. + {ui::DomCode::DIGIT1, + {true, ui::DomKey::CHARACTER, '1', ui::VKEY_1}, + {true, ui::DomKey::UNIDENTIFIED, 0, ui::VKEY_1}}, + {ui::DomCode::EQUAL, + {true, ui::DomKey::CHARACTER, '=', ui::VKEY_OEM_PLUS}, + {true, ui::DomKey::UNIDENTIFIED, 0, ui::VKEY_OEM_PLUS}}, + {ui::DomCode::TAB, + {true, ui::DomKey::TAB, 9, ui::VKEY_TAB}, + {true, ui::DomKey::UNIDENTIFIED, 0, ui::VKEY_TAB}}, + {ui::DomCode::F1, + {true, ui::DomKey::F1, 0, ui::VKEY_F1}, + {true, ui::DomKey::UNIDENTIFIED, 0, ui::VKEY_F1}}, + {ui::DomCode::VOLUME_UP, + {true, ui::DomKey::VOLUME_UP, 0, ui::VKEY_VOLUME_UP}, + {true, ui::DomKey::UNIDENTIFIED, 0, ui::VKEY_VOLUME_UP}}, + }; + for (const auto& it : kNonControlCharacters) { + // Verify |DomCodeToControlCharacter()|. + CheckDomCodeToMeaning("n_cc_n", ui::DomCodeToControlCharacter, it.dom_code, + ui::EF_NONE, kUndefined); + CheckDomCodeToMeaning("n_cc_c", ui::DomCodeToControlCharacter, it.dom_code, + ui::EF_CONTROL_DOWN, kUndefined); + CheckDomCodeToMeaning("n_cc_cs", ui::DomCodeToControlCharacter, it.dom_code, + ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN, kUndefined); + // Verify |DomCodeToUsLayoutMeaning()|. + CheckDomCodeToMeaning("n_us_n", ui::DomCodeToUsLayoutMeaning, it.dom_code, + ui::EF_NONE, it.normal); + CheckDomCodeToMeaning("n_us_c", ui::DomCodeToUsLayoutMeaning, it.dom_code, + ui::EF_CONTROL_DOWN, it.control); + CheckDomCodeToMeaning("n_us_cs", ui::DomCodeToUsLayoutMeaning, it.dom_code, + ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN, it.control); + } +} + +TEST(KeyboardCodeConversion, UsLayout) { + static const struct { + ui::DomCode dom_code; + Meaning normal; + Meaning shift; + } kPrintableUsLayout[] = { + {ui::DomCode::KEY_A, + {true, ui::DomKey::CHARACTER, 'a', ui::VKEY_A}, + {true, ui::DomKey::CHARACTER, 'A', ui::VKEY_A}}, + {ui::DomCode::KEY_B, + {true, ui::DomKey::CHARACTER, 'b', ui::VKEY_B}, + {true, ui::DomKey::CHARACTER, 'B', ui::VKEY_B}}, + {ui::DomCode::KEY_C, + {true, ui::DomKey::CHARACTER, 'c', ui::VKEY_C}, + {true, ui::DomKey::CHARACTER, 'C', ui::VKEY_C}}, + {ui::DomCode::KEY_D, + {true, ui::DomKey::CHARACTER, 'd', ui::VKEY_D}, + {true, ui::DomKey::CHARACTER, 'D', ui::VKEY_D}}, + {ui::DomCode::KEY_E, + {true, ui::DomKey::CHARACTER, 'e', ui::VKEY_E}, + {true, ui::DomKey::CHARACTER, 'E', ui::VKEY_E}}, + {ui::DomCode::KEY_F, + {true, ui::DomKey::CHARACTER, 'f', ui::VKEY_F}, + {true, ui::DomKey::CHARACTER, 'F', ui::VKEY_F}}, + {ui::DomCode::KEY_G, + {true, ui::DomKey::CHARACTER, 'g', ui::VKEY_G}, + {true, ui::DomKey::CHARACTER, 'G', ui::VKEY_G}}, + {ui::DomCode::KEY_H, + {true, ui::DomKey::CHARACTER, 'h', ui::VKEY_H}, + {true, ui::DomKey::CHARACTER, 'H', ui::VKEY_H}}, + {ui::DomCode::KEY_I, + {true, ui::DomKey::CHARACTER, 'i', ui::VKEY_I}, + {true, ui::DomKey::CHARACTER, 'I', ui::VKEY_I}}, + {ui::DomCode::KEY_J, + {true, ui::DomKey::CHARACTER, 'j', ui::VKEY_J}, + {true, ui::DomKey::CHARACTER, 'J', ui::VKEY_J}}, + {ui::DomCode::KEY_K, + {true, ui::DomKey::CHARACTER, 'k', ui::VKEY_K}, + {true, ui::DomKey::CHARACTER, 'K', ui::VKEY_K}}, + {ui::DomCode::KEY_L, + {true, ui::DomKey::CHARACTER, 'l', ui::VKEY_L}, + {true, ui::DomKey::CHARACTER, 'L', ui::VKEY_L}}, + {ui::DomCode::KEY_M, + {true, ui::DomKey::CHARACTER, 'm', ui::VKEY_M}, + {true, ui::DomKey::CHARACTER, 'M', ui::VKEY_M}}, + {ui::DomCode::KEY_N, + {true, ui::DomKey::CHARACTER, 'n', ui::VKEY_N}, + {true, ui::DomKey::CHARACTER, 'N', ui::VKEY_N}}, + {ui::DomCode::KEY_O, + {true, ui::DomKey::CHARACTER, 'o', ui::VKEY_O}, + {true, ui::DomKey::CHARACTER, 'O', ui::VKEY_O}}, + {ui::DomCode::KEY_P, + {true, ui::DomKey::CHARACTER, 'p', ui::VKEY_P}, + {true, ui::DomKey::CHARACTER, 'P', ui::VKEY_P}}, + {ui::DomCode::KEY_Q, + {true, ui::DomKey::CHARACTER, 'q', ui::VKEY_Q}, + {true, ui::DomKey::CHARACTER, 'Q', ui::VKEY_Q}}, + {ui::DomCode::KEY_R, + {true, ui::DomKey::CHARACTER, 'r', ui::VKEY_R}, + {true, ui::DomKey::CHARACTER, 'R', ui::VKEY_R}}, + {ui::DomCode::KEY_S, + {true, ui::DomKey::CHARACTER, 's', ui::VKEY_S}, + {true, ui::DomKey::CHARACTER, 'S', ui::VKEY_S}}, + {ui::DomCode::KEY_T, + {true, ui::DomKey::CHARACTER, 't', ui::VKEY_T}, + {true, ui::DomKey::CHARACTER, 'T', ui::VKEY_T}}, + {ui::DomCode::KEY_U, + {true, ui::DomKey::CHARACTER, 'u', ui::VKEY_U}, + {true, ui::DomKey::CHARACTER, 'U', ui::VKEY_U}}, + {ui::DomCode::KEY_V, + {true, ui::DomKey::CHARACTER, 'v', ui::VKEY_V}, + {true, ui::DomKey::CHARACTER, 'V', ui::VKEY_V}}, + {ui::DomCode::KEY_W, + {true, ui::DomKey::CHARACTER, 'w', ui::VKEY_W}, + {true, ui::DomKey::CHARACTER, 'W', ui::VKEY_W}}, + {ui::DomCode::KEY_X, + {true, ui::DomKey::CHARACTER, 'x', ui::VKEY_X}, + {true, ui::DomKey::CHARACTER, 'X', ui::VKEY_X}}, + {ui::DomCode::KEY_Y, + {true, ui::DomKey::CHARACTER, 'y', ui::VKEY_Y}, + {true, ui::DomKey::CHARACTER, 'Y', ui::VKEY_Y}}, + {ui::DomCode::KEY_Z, + {true, ui::DomKey::CHARACTER, 'z', ui::VKEY_Z}, + {true, ui::DomKey::CHARACTER, 'Z', ui::VKEY_Z}}, + {ui::DomCode::DIGIT1, + {true, ui::DomKey::CHARACTER, '1', ui::VKEY_1}, + {true, ui::DomKey::CHARACTER, '!', ui::VKEY_1}}, + {ui::DomCode::DIGIT2, + {true, ui::DomKey::CHARACTER, '2', ui::VKEY_2}, + {true, ui::DomKey::CHARACTER, '@', ui::VKEY_2}}, + {ui::DomCode::DIGIT3, + {true, ui::DomKey::CHARACTER, '3', ui::VKEY_3}, + {true, ui::DomKey::CHARACTER, '#', ui::VKEY_3}}, + {ui::DomCode::DIGIT4, + {true, ui::DomKey::CHARACTER, '4', ui::VKEY_4}, + {true, ui::DomKey::CHARACTER, '$', ui::VKEY_4}}, + {ui::DomCode::DIGIT5, + {true, ui::DomKey::CHARACTER, '5', ui::VKEY_5}, + {true, ui::DomKey::CHARACTER, '%', ui::VKEY_5}}, + {ui::DomCode::DIGIT6, + {true, ui::DomKey::CHARACTER, '6', ui::VKEY_6}, + {true, ui::DomKey::CHARACTER, '^', ui::VKEY_6}}, + {ui::DomCode::DIGIT7, + {true, ui::DomKey::CHARACTER, '7', ui::VKEY_7}, + {true, ui::DomKey::CHARACTER, '&', ui::VKEY_7}}, + {ui::DomCode::DIGIT8, + {true, ui::DomKey::CHARACTER, '8', ui::VKEY_8}, + {true, ui::DomKey::CHARACTER, '*', ui::VKEY_8}}, + {ui::DomCode::DIGIT9, + {true, ui::DomKey::CHARACTER, '9', ui::VKEY_9}, + {true, ui::DomKey::CHARACTER, '(', ui::VKEY_9}}, + {ui::DomCode::DIGIT0, + {true, ui::DomKey::CHARACTER, '0', ui::VKEY_0}, + {true, ui::DomKey::CHARACTER, ')', ui::VKEY_0}}, + {ui::DomCode::SPACE, + {true, ui::DomKey::CHARACTER, ' ', ui::VKEY_SPACE}, + {true, ui::DomKey::CHARACTER, ' ', ui::VKEY_SPACE}}, + {ui::DomCode::MINUS, + {true, ui::DomKey::CHARACTER, '-', ui::VKEY_OEM_MINUS}, + {true, ui::DomKey::CHARACTER, '_', ui::VKEY_OEM_MINUS}}, + {ui::DomCode::EQUAL, + {true, ui::DomKey::CHARACTER, '=', ui::VKEY_OEM_PLUS}, + {true, ui::DomKey::CHARACTER, '+', ui::VKEY_OEM_PLUS}}, + {ui::DomCode::BRACKET_LEFT, + {true, ui::DomKey::CHARACTER, '[', ui::VKEY_OEM_4}, + {true, ui::DomKey::CHARACTER, '{', ui::VKEY_OEM_4}}, + {ui::DomCode::BRACKET_RIGHT, + {true, ui::DomKey::CHARACTER, ']', ui::VKEY_OEM_6}, + {true, ui::DomKey::CHARACTER, '}', ui::VKEY_OEM_6}}, + {ui::DomCode::BACKSLASH, + {true, ui::DomKey::CHARACTER, '\\', ui::VKEY_OEM_5}, + {true, ui::DomKey::CHARACTER, '|', ui::VKEY_OEM_5}}, + {ui::DomCode::SEMICOLON, + {true, ui::DomKey::CHARACTER, ';', ui::VKEY_OEM_1}, + {true, ui::DomKey::CHARACTER, ':', ui::VKEY_OEM_1}}, + {ui::DomCode::QUOTE, + {true, ui::DomKey::CHARACTER, '\'', ui::VKEY_OEM_7}, + {true, ui::DomKey::CHARACTER, '"', ui::VKEY_OEM_7}}, + {ui::DomCode::BACKQUOTE, + {true, ui::DomKey::CHARACTER, '`', ui::VKEY_OEM_3}, + {true, ui::DomKey::CHARACTER, '~', ui::VKEY_OEM_3}}, + {ui::DomCode::COMMA, + {true, ui::DomKey::CHARACTER, ',', ui::VKEY_OEM_COMMA}, + {true, ui::DomKey::CHARACTER, '<', ui::VKEY_OEM_COMMA}}, + {ui::DomCode::PERIOD, + {true, ui::DomKey::CHARACTER, '.', ui::VKEY_OEM_PERIOD}, + {true, ui::DomKey::CHARACTER, '>', ui::VKEY_OEM_PERIOD}}, + {ui::DomCode::SLASH, + {true, ui::DomKey::CHARACTER, '/', ui::VKEY_OEM_2}, + {true, ui::DomKey::CHARACTER, '?', ui::VKEY_OEM_2}}, + {ui::DomCode::INTL_BACKSLASH, + {true, ui::DomKey::CHARACTER, '\\', ui::VKEY_OEM_102}, + {true, ui::DomKey::CHARACTER, '|', ui::VKEY_OEM_102}}, + {ui::DomCode::INTL_YEN, + {true, ui::DomKey::CHARACTER, 0x00A5, ui::VKEY_OEM_5}, + {true, ui::DomKey::CHARACTER, '|', ui::VKEY_OEM_5}}, + {ui::DomCode::NUMPAD_DIVIDE, + {true, ui::DomKey::CHARACTER, '/', ui::VKEY_DIVIDE}, + {true, ui::DomKey::CHARACTER, '/', ui::VKEY_DIVIDE}}, + {ui::DomCode::NUMPAD_MULTIPLY, + {true, ui::DomKey::CHARACTER, '*', ui::VKEY_MULTIPLY}, + {true, ui::DomKey::CHARACTER, '*', ui::VKEY_MULTIPLY}}, + {ui::DomCode::NUMPAD_SUBTRACT, + {true, ui::DomKey::CHARACTER, '-', ui::VKEY_SUBTRACT}, + {true, ui::DomKey::CHARACTER, '-', ui::VKEY_SUBTRACT}}, + {ui::DomCode::NUMPAD_ADD, + {true, ui::DomKey::CHARACTER, '+', ui::VKEY_ADD}, + {true, ui::DomKey::CHARACTER, '+', ui::VKEY_ADD}}, + {ui::DomCode::NUMPAD1, + {true, ui::DomKey::CHARACTER, '1', ui::VKEY_1}, + {true, ui::DomKey::CHARACTER, '1', ui::VKEY_1}}, + {ui::DomCode::NUMPAD2, + {true, ui::DomKey::CHARACTER, '2', ui::VKEY_2}, + {true, ui::DomKey::CHARACTER, '2', ui::VKEY_2}}, + {ui::DomCode::NUMPAD3, + {true, ui::DomKey::CHARACTER, '3', ui::VKEY_3}, + {true, ui::DomKey::CHARACTER, '3', ui::VKEY_3}}, + {ui::DomCode::NUMPAD4, + {true, ui::DomKey::CHARACTER, '4', ui::VKEY_4}, + {true, ui::DomKey::CHARACTER, '4', ui::VKEY_4}}, + {ui::DomCode::NUMPAD5, + {true, ui::DomKey::CHARACTER, '5', ui::VKEY_5}, + {true, ui::DomKey::CHARACTER, '5', ui::VKEY_5}}, + {ui::DomCode::NUMPAD6, + {true, ui::DomKey::CHARACTER, '6', ui::VKEY_6}, + {true, ui::DomKey::CHARACTER, '6', ui::VKEY_6}}, + {ui::DomCode::NUMPAD7, + {true, ui::DomKey::CHARACTER, '7', ui::VKEY_7}, + {true, ui::DomKey::CHARACTER, '7', ui::VKEY_7}}, + {ui::DomCode::NUMPAD8, + {true, ui::DomKey::CHARACTER, '8', ui::VKEY_8}, + {true, ui::DomKey::CHARACTER, '8', ui::VKEY_8}}, + {ui::DomCode::NUMPAD9, + {true, ui::DomKey::CHARACTER, '9', ui::VKEY_9}, + {true, ui::DomKey::CHARACTER, '9', ui::VKEY_9}}, + {ui::DomCode::NUMPAD0, + {true, ui::DomKey::CHARACTER, '0', ui::VKEY_0}, + {true, ui::DomKey::CHARACTER, '0', ui::VKEY_0}}, + {ui::DomCode::NUMPAD_DECIMAL, + {true, ui::DomKey::CHARACTER, '.', ui::VKEY_DECIMAL}, + {true, ui::DomKey::CHARACTER, '.', ui::VKEY_DECIMAL}}, + {ui::DomCode::NUMPAD_EQUAL, + {true, ui::DomKey::CHARACTER, '=', ui::VKEY_UNKNOWN}, + {true, ui::DomKey::CHARACTER, '=', ui::VKEY_UNKNOWN}}, + {ui::DomCode::NUMPAD_COMMA, + {true, ui::DomKey::CHARACTER, ',', ui::VKEY_OEM_COMMA}, + {true, ui::DomKey::CHARACTER, ',', ui::VKEY_OEM_COMMA}}, + {ui::DomCode::NUMPAD_PAREN_LEFT, + {true, ui::DomKey::CHARACTER, '(', ui::VKEY_UNKNOWN}, + {true, ui::DomKey::CHARACTER, '(', ui::VKEY_UNKNOWN}}, + {ui::DomCode::NUMPAD_PAREN_RIGHT, + {true, ui::DomKey::CHARACTER, ')', ui::VKEY_UNKNOWN}, + {true, ui::DomKey::CHARACTER, ')', ui::VKEY_UNKNOWN}}, + {ui::DomCode::NUMPAD_SIGN_CHANGE, + {true, ui::DomKey::CHARACTER, 0xB1, ui::VKEY_UNKNOWN}, + {true, ui::DomKey::CHARACTER, 0xB1, ui::VKEY_UNKNOWN}}, + }; + + for (const auto& it : kPrintableUsLayout) { + CheckDomCodeToMeaning("p_us_n", ui::DomCodeToUsLayoutMeaning, it.dom_code, + ui::EF_NONE, it.normal); + CheckDomCodeToMeaning("p_us_s", ui::DomCodeToUsLayoutMeaning, it.dom_code, + ui::EF_SHIFT_DOWN, it.shift); + CheckDomCodeToMeaning("p_us_a", ui::DomCodeToUsLayoutMeaning, it.dom_code, + ui::EF_ALTGR_DOWN, it.normal); + CheckDomCodeToMeaning("p_us_a", ui::DomCodeToUsLayoutMeaning, it.dom_code, + ui::EF_ALTGR_DOWN|ui::EF_SHIFT_DOWN, it.shift); + } +} + +} // namespace
diff --git a/ui/events/latency_info.cc b/ui/events/latency_info.cc index 9718aa4..112cc462 100644 --- a/ui/events/latency_info.cc +++ b/ui/events/latency_info.cc
@@ -2,14 +2,16 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "ui/events/latency_info.h" + +#include <algorithm> +#include <string> + #include "base/json/json_writer.h" #include "base/lazy_instance.h" #include "base/memory/scoped_ptr.h" #include "base/strings/stringprintf.h" #include "base/trace_event/trace_event.h" -#include "ui/events/latency_info.h" - -#include <algorithm> namespace { @@ -216,8 +218,17 @@ void LatencyInfo::AddLatencyNumber(LatencyComponentType component, int64 id, int64 component_sequence_number) { - AddLatencyNumberWithTimestamp(component, id, component_sequence_number, - base::TimeTicks::Now(), 1); + AddLatencyNumberWithTimestampImpl(component, id, component_sequence_number, + base::TimeTicks::Now(), 1, nullptr); +} + +void LatencyInfo::AddLatencyNumberWithTraceName( + LatencyComponentType component, + int64 id, + int64 component_sequence_number, + const char* trace_name_str) { + AddLatencyNumberWithTimestampImpl(component, id, component_sequence_number, + base::TimeTicks::Now(), 1, trace_name_str); } void LatencyInfo::AddLatencyNumberWithTimestamp(LatencyComponentType component, @@ -225,10 +236,24 @@ int64 component_sequence_number, base::TimeTicks time, uint32 event_count) { + AddLatencyNumberWithTimestampImpl(component, id, component_sequence_number, + time, event_count, nullptr); +} + +void LatencyInfo::AddLatencyNumberWithTimestampImpl( + LatencyComponentType component, + int64 id, + int64 component_sequence_number, + base::TimeTicks time, + uint32 event_count, + const char* trace_name_str) { const unsigned char* benchmark_enabled = g_benchmark_enabled.Get().benchmark_enabled; + if (*benchmark_enabled && trace_name_str) + trace_name = trace_name_str; + if (IsBeginComponent(component)) { // Should only ever add begin component once. CHECK_EQ(-1, trace_id); @@ -260,9 +285,9 @@ } else { ts = base::TimeTicks::NowFromSystemTraceTime().ToInternalValue(); } - TRACE_EVENT_ASYNC_BEGIN_WITH_TIMESTAMP0( + TRACE_EVENT_COPY_ASYNC_BEGIN_WITH_TIMESTAMP0( "benchmark", - "InputLatency", + ("InputLatency::" + trace_name).c_str(), TRACE_ID_DONT_MANGLE(trace_id), ts); } @@ -296,10 +321,10 @@ terminated = true; if (*benchmark_enabled) { - TRACE_EVENT_ASYNC_END1("benchmark", - "InputLatency", - TRACE_ID_DONT_MANGLE(trace_id), - "data", AsTraceableData(*this)); + TRACE_EVENT_COPY_ASYNC_END1("benchmark", + ("InputLatency::" + trace_name).c_str(), + TRACE_ID_DONT_MANGLE(trace_id), + "data", AsTraceableData(*this)); } TRACE_EVENT_FLOW_END0( @@ -336,11 +361,4 @@ latency_components.clear(); } -void LatencyInfo::TraceEventType(const char* event_type) { - TRACE_EVENT_ASYNC_STEP_INTO0("benchmark", - "InputLatency", - TRACE_ID_DONT_MANGLE(trace_id), - event_type); -} - } // namespace ui
diff --git a/ui/events/latency_info.h b/ui/events/latency_info.h index eaac5ce..1c6ceef 100644 --- a/ui/events/latency_info.h +++ b/ui/events/latency_info.h
@@ -5,6 +5,7 @@ #ifndef UI_EVENTS_LATENCY_INFO_H_ #define UI_EVENTS_LATENCY_INFO_H_ +#include <string> #include <utility> #include <vector> @@ -143,6 +144,14 @@ int64 id, int64 component_sequence_number); + // Similar to |AddLatencyNumber|, and also appends |trace_name_str| to + // the trace event's name. + // This function should only be called when adding a BEGIN component. + void AddLatencyNumberWithTraceName(LatencyComponentType component, + int64 id, + int64 component_sequence_number, + const char* trace_name_str); + // Modifies the current sequence number and adds a certain number of events // for a specific component. void AddLatencyNumberWithTimestamp(LatencyComponentType component, @@ -151,6 +160,13 @@ base::TimeTicks time, uint32 event_count); + void AddLatencyNumberWithTimestampImpl(LatencyComponentType component, + int64 id, + int64 component_sequence_number, + base::TimeTicks time, + uint32 event_count, + const char* trace_name_str); + // Returns true if the a component with |type| and |id| is found in // the latency_components and the component is stored to |output| if // |output| is not NULL. Returns false if no such component is found. @@ -162,8 +178,9 @@ void Clear(); - // Records the |event_type| in trace buffer as TRACE_EVENT_ASYNC_STEP. - void TraceEventType(const char* event_type); + // Shown as part of the name of the trace event for this LatencyInfo. + // String is empty if no tracing is enabled. + std::string trace_name; LatencyMap latency_components;
diff --git a/ui/events/latency_info_nacl.gyp b/ui/events/latency_info_nacl.gyp index c44e499..1b7e402 100644 --- a/ui/events/latency_info_nacl.gyp +++ b/ui/events/latency_info_nacl.gyp
@@ -23,7 +23,6 @@ '../../base/base_nacl.gyp:base_nacl_nonsfi', '../../ipc/ipc_nacl.gyp:ipc_nacl', '../../ipc/ipc_nacl.gyp:ipc_nacl_nonsfi', - '../../native_client/tools.gyp:prep_toolchain' ], 'variables': { 'nacl_untrusted_build': 1,
diff --git a/ui/events/ozone/device/udev/device_manager_udev.cc b/ui/events/ozone/device/udev/device_manager_udev.cc index 884e418..4e28e39 100644 --- a/ui/events/ozone/device/udev/device_manager_udev.cc +++ b/ui/events/ozone/device/udev/device_manager_udev.cc
@@ -134,7 +134,7 @@ void DeviceManagerUdev::OnFileCanReadWithoutBlocking(int fd) { // The netlink socket should never become disconnected. There's no need // to handle broken connections here. - TRACE_EVENT1("ozone", "UdevDeviceChange", "socket", fd); + TRACE_EVENT1("evdev", "UdevDeviceChange", "socket", fd); device::ScopedUdevDevicePtr device( device::udev_monitor_receive_device(monitor_.get()));
diff --git a/ui/events/ozone/evdev/event_factory_evdev.cc b/ui/events/ozone/evdev/event_factory_evdev.cc index e9098db41..6eb45d9 100644 --- a/ui/events/ozone/evdev/event_factory_evdev.cc +++ b/ui/events/ozone/evdev/event_factory_evdev.cc
@@ -150,12 +150,16 @@ } void EventFactoryEvdev::DispatchKeyEvent(const KeyEventParams& params) { + TRACE_EVENT1("evdev", "EventFactoryEvdev::DispatchKeyEvent", "device", + params.device_id); keyboard_.OnKeyChange(params.code, params.down, params.timestamp, params.device_id); } void EventFactoryEvdev::DispatchMouseMoveEvent( const MouseMoveEventParams& params) { + TRACE_EVENT1("evdev", "EventFactoryEvdev::DispatchMouseMoveEvent", "device", + params.device_id); MouseEvent event(ui::ET_MOUSE_MOVED, params.location, params.location, params.timestamp, modifiers_.GetModifierFlags(), /* changed_button_flags */ 0); @@ -165,6 +169,9 @@ void EventFactoryEvdev::DispatchMouseButtonEvent( const MouseButtonEventParams& params) { + TRACE_EVENT1("evdev", "EventFactoryEvdev::DispatchMouseButtonEvent", "device", + params.device_id); + // Mouse buttons can be remapped, touchpad taps & clicks cannot. unsigned int button = params.button; if (params.allow_remap) @@ -204,6 +211,8 @@ void EventFactoryEvdev::DispatchMouseWheelEvent( const MouseWheelEventParams& params) { + TRACE_EVENT1("evdev", "EventFactoryEvdev::DispatchMouseWheelEvent", "device", + params.device_id); MouseWheelEvent event(params.delta, params.location, params.location, params.timestamp, modifiers_.GetModifierFlags(), 0 /* changed_button_flags */); @@ -212,6 +221,8 @@ } void EventFactoryEvdev::DispatchScrollEvent(const ScrollEventParams& params) { + TRACE_EVENT1("evdev", "EventFactoryEvdev::DispatchScrollEvent", "device", + params.device_id); ScrollEvent event(params.type, params.location, params.timestamp, modifiers_.GetModifierFlags(), params.delta.x(), params.delta.y(), params.ordinal_delta.x(), @@ -221,6 +232,9 @@ } void EventFactoryEvdev::DispatchTouchEvent(const TouchEventParams& params) { + TRACE_EVENT1("evdev", "EventFactoryEvdev::DispatchTouchEvent", "device", + params.device_id); + float x = params.location.x(); float y = params.location.y(); double radius_x = params.radii.x(); @@ -257,18 +271,22 @@ void EventFactoryEvdev::DispatchKeyboardDevicesUpdated( const std::vector<KeyboardDevice>& devices) { + TRACE_EVENT0("evdev", "EventFactoryEvdev::DispatchKeyboardDevicesUpdated"); DeviceHotplugEventObserver* observer = DeviceDataManager::GetInstance(); observer->OnKeyboardDevicesUpdated(devices); } void EventFactoryEvdev::DispatchTouchscreenDevicesUpdated( const std::vector<TouchscreenDevice>& devices) { + TRACE_EVENT0("evdev", "EventFactoryEvdev::DispatchTouchscreenDevicesUpdated"); DeviceHotplugEventObserver* observer = DeviceDataManager::GetInstance(); observer->OnTouchscreenDevicesUpdated(devices); } void EventFactoryEvdev::DispatchMouseDevicesUpdated( const std::vector<InputDevice>& devices) { + TRACE_EVENT0("evdev", "EventFactoryEvdev::DispatchMouseDevicesUpdated"); + // There's no list of mice in DeviceDataManager. input_controller_.set_has_mouse(devices.size() != 0); DeviceHotplugEventObserver* observer = DeviceDataManager::GetInstance(); @@ -277,6 +295,8 @@ void EventFactoryEvdev::DispatchTouchpadDevicesUpdated( const std::vector<InputDevice>& devices) { + TRACE_EVENT0("evdev", "EventFactoryEvdev::DispatchTouchpadDevicesUpdated"); + // There's no list of touchpads in DeviceDataManager. input_controller_.set_has_touchpad(devices.size() != 0); DeviceHotplugEventObserver* observer = DeviceDataManager::GetInstance(); @@ -291,12 +311,14 @@ switch (event.action_type()) { case DeviceEvent::ADD: case DeviceEvent::CHANGE: { - TRACE_EVENT1("ozone", "OnDeviceAdded", "path", event.path().value()); + TRACE_EVENT1("evdev", "EventFactoryEvdev::OnDeviceAdded", "path", + event.path().value()); input_device_factory_proxy_->AddInputDevice(NextDeviceId(), event.path()); break; } case DeviceEvent::REMOVE: { - TRACE_EVENT1("ozone", "OnDeviceRemoved", "path", event.path().value()); + TRACE_EVENT1("evdev", "EventFactoryEvdev::OnDeviceRemoved", "path", + event.path().value()); input_device_factory_proxy_->RemoveInputDevice(event.path()); break; } @@ -339,6 +361,7 @@ void EventFactoryEvdev::OnThreadStarted( scoped_ptr<InputDeviceFactoryEvdevProxy> input_device_factory) { + TRACE_EVENT0("evdev", "EventFactoryEvdev::OnThreadStarted"); input_device_factory_proxy_ = input_device_factory.Pass(); // Hook up device configuration.
diff --git a/ui/events/ozone/evdev/event_thread_evdev.cc b/ui/events/ozone/evdev/event_thread_evdev.cc index d6c5ffc..efee406 100644 --- a/ui/events/ozone/evdev/event_thread_evdev.cc +++ b/ui/events/ozone/evdev/event_thread_evdev.cc
@@ -33,7 +33,7 @@ ~EvdevThread() override { Stop(); } void Init() override { - TRACE_EVENT0("ozone", "EvdevThread::Init"); + TRACE_EVENT0("evdev", "EvdevThread::Init"); input_device_factory_ = new InputDeviceFactoryEvdev(dispatcher_.Pass(), cursor_); @@ -46,7 +46,7 @@ } void CleanUp() override { - TRACE_EVENT0("ozone", "EvdevThread::CleanUp"); + TRACE_EVENT0("evdev", "EvdevThread::CleanUp"); delete input_device_factory_; } @@ -72,7 +72,7 @@ void EventThreadEvdev::Start(scoped_ptr<DeviceEventDispatcherEvdev> dispatcher, CursorDelegateEvdev* cursor, const EventThreadStartCallback& callback) { - TRACE_EVENT0("ozone", "EventThreadEvdev::Start"); + TRACE_EVENT0("evdev", "EventThreadEvdev::Start"); thread_.reset(new EvdevThread(dispatcher.Pass(), cursor, callback)); if (!thread_->StartWithOptions( base::Thread::Options(base::MessageLoop::TYPE_UI, 0)))
diff --git a/ui/events/ozone/evdev/input_device_factory_evdev.cc b/ui/events/ozone/evdev/input_device_factory_evdev.cc index d8997ca..11a8f1e 100644 --- a/ui/events/ozone/evdev/input_device_factory_evdev.cc +++ b/ui/events/ozone/evdev/input_device_factory_evdev.cc
@@ -130,7 +130,7 @@ const base::FilePath& path = params->path; scoped_ptr<EventConverterEvdev> converter; - TRACE_EVENT1("ozone", "OpenInputDevice", "path", path.value()); + TRACE_EVENT1("evdev", "OpenInputDevice", "path", path.value()); int fd = open(path.value().c_str(), O_RDWR | O_NONBLOCK); if (fd < 0) { @@ -170,7 +170,7 @@ // run it on the FILE thread. void CloseInputDevice(const base::FilePath& path, scoped_ptr<EventConverterEvdev> converter) { - TRACE_EVENT1("ozone", "CloseInputDevice", "path", path.value()); + TRACE_EVENT1("evdev", "CloseInputDevice", "path", path.value()); converter.reset(); } @@ -232,7 +232,7 @@ if (converter.get()) { const base::FilePath& path = converter->path(); - TRACE_EVENT1("ozone", "AttachInputDevice", "path", path.value()); + TRACE_EVENT1("evdev", "AttachInputDevice", "path", path.value()); DCHECK(task_runner_->RunsTasksOnCurrentThread()); // If we have an existing device, detach it. We don't want two @@ -255,7 +255,7 @@ } void InputDeviceFactoryEvdev::DetachInputDevice(const base::FilePath& path) { - TRACE_EVENT1("ozone", "DetachInputDevice", "path", path.value()); + TRACE_EVENT1("evdev", "DetachInputDevice", "path", path.value()); DCHECK(task_runner_->RunsTasksOnCurrentThread()); // Remove device from map.
diff --git a/ui/events/ozone/evdev/touch_event_converter_evdev.cc b/ui/events/ozone/evdev/touch_event_converter_evdev.cc index b4c09292..5b7b2596 100644 --- a/ui/events/ozone/evdev/touch_event_converter_evdev.cc +++ b/ui/events/ozone/evdev/touch_event_converter_evdev.cc
@@ -28,6 +28,7 @@ #include "ui/events/event.h" #include "ui/events/event_constants.h" #include "ui/events/event_switches.h" +#include "ui/events/event_utils.h" #include "ui/events/ozone/evdev/device_event_dispatcher_evdev.h" #include "ui/events/ozone/evdev/touch_evdev_types.h" #include "ui/events/ozone/evdev/touch_noise/touch_noise_finder.h" @@ -74,6 +75,8 @@ } } +const int kTrackingIdForUnusedSlot = -1; + } // namespace namespace ui { @@ -154,8 +157,8 @@ for (size_t i = 0; i < events_.size(); ++i) { events_[i].x = info.GetAbsMtSlotValueWithDefault(ABS_MT_POSITION_X, i, 0); events_[i].y = info.GetAbsMtSlotValueWithDefault(ABS_MT_POSITION_Y, i, 0); - events_[i].tracking_id = - info.GetAbsMtSlotValueWithDefault(ABS_MT_TRACKING_ID, i, -1); + events_[i].tracking_id = info.GetAbsMtSlotValueWithDefault( + ABS_MT_TRACKING_ID, i, kTrackingIdForUnusedSlot); events_[i].touching = (events_[i].tracking_id >= 0); events_[i].slot = i; @@ -176,7 +179,7 @@ // TODO(spang): Add key state to EventDeviceInfo to allow initial contact. events_[0].x = 0; events_[0].y = 0; - events_[0].tracking_id = -1; + events_[0].tracking_id = kTrackingIdForUnusedSlot; events_[0].touching = false; events_[0].slot = 0; events_[0].radius_x = 0; @@ -206,6 +209,10 @@ return touch_points_; } +void TouchEventConverterEvdev::OnStopped() { + ReleaseTouches(); +} + void TouchEventConverterEvdev::OnFileCanReadWithoutBlocking(int fd) { input_event inputs[kNumTouchEvdevSlots * 6 + 1]; ssize_t read_size = read(fd, inputs, sizeof(inputs)); @@ -263,7 +270,8 @@ } else if (event.type == EV_KEY && event.code == BTN_TOUCH) { emulated_event.type = EV_ABS; emulated_event.code = ABS_MT_TRACKING_ID; - emulated_event.value = event.value ? NextTrackingId() : -1; + emulated_event.value = + event.value ? NextTrackingId() : kTrackingIdForUnusedSlot; ProcessMultitouchEvent(emulated_event); } } @@ -295,13 +303,7 @@ events_[current_slot_].y = input.value; break; case ABS_MT_TRACKING_ID: - if (input.value < 0) { - events_[current_slot_].touching = false; - } else { - events_[current_slot_].touching = true; - events_[current_slot_].cancelled = false; - } - events_[current_slot_].tracking_id = input.value; + UpdateTrackingId(current_slot_, input.value); break; case ABS_MT_PRESSURE: events_[current_slot_].pressure = ScalePressure(input.value); @@ -392,6 +394,27 @@ } } +void TouchEventConverterEvdev::UpdateTrackingId(int slot, int tracking_id) { + InProgressTouchEvdev* event = &events_[slot]; + + if (event->tracking_id == tracking_id) + return; + + event->tracking_id = tracking_id; + event->touching = (tracking_id >= 0); + event->altered = true; + + if (tracking_id >= 0) + event->cancelled = false; +} + +void TouchEventConverterEvdev::ReleaseTouches() { + for (size_t slot = 0; slot < events_.size(); slot++) + UpdateTrackingId(slot, kTrackingIdForUnusedSlot); + + ReportEvents(EventTimeForNow()); +} + float TouchEventConverterEvdev::ScalePressure(int32_t value) { float pressure = value - pressure_min_; if (pressure_max_ - pressure_min_)
diff --git a/ui/events/ozone/evdev/touch_event_converter_evdev.h b/ui/events/ozone/evdev/touch_event_converter_evdev.h index 6012f86c..7cd7576a2 100644 --- a/ui/events/ozone/evdev/touch_event_converter_evdev.h +++ b/ui/events/ozone/evdev/touch_event_converter_evdev.h
@@ -38,6 +38,7 @@ bool HasTouchscreen() const override; gfx::Size GetTouchscreenSize() const override; int GetTouchPoints() const override; + void OnStopped() override; // Unsafe part of initialization. virtual void Initialize(const EventDeviceInfo& info); @@ -65,6 +66,9 @@ const base::TimeDelta& delta); void ReportEvents(base::TimeDelta delta); + void UpdateTrackingId(int slot, int tracking_id); + void ReleaseTouches(); + // Normalize pressure value to [0, 1]. float ScalePressure(int32_t value);
diff --git a/ui/events/ozone/evdev/touch_event_converter_evdev_unittest.cc b/ui/events/ozone/evdev/touch_event_converter_evdev_unittest.cc index 59a5f48..77eeef6 100644 --- a/ui/events/ozone/evdev/touch_event_converter_evdev_unittest.cc +++ b/ui/events/ozone/evdev/touch_event_converter_evdev_unittest.cc
@@ -168,20 +168,20 @@ dispatcher_.reset(new ui::MockDeviceEventDispatcherEvdev( base::Bind(&TouchEventConverterEvdevTest::DispatchCallback, base::Unretained(this)))); - device_ = new ui::MockTouchEventConverterEvdev( + device_.reset(new ui::MockTouchEventConverterEvdev( events_in_, base::FilePath(kTestDevicePath), devinfo, - dispatcher_.get()); + dispatcher_.get())); loop_ = new base::MessageLoopForUI; ui::DeviceDataManager::CreateInstance(); } void TearDown() override { - delete device_; + device_.reset(); delete loop_; } - ui::MockTouchEventConverterEvdev* device() { return device_; } + ui::MockTouchEventConverterEvdev* device() { return device_.get(); } unsigned size() { return dispatched_events_.size(); } const ui::TouchEventParams& dispatched_event(unsigned index) { @@ -191,9 +191,11 @@ void ClearDispatchedEvents() { dispatched_events_.clear(); } + void DestroyDevice() { device_.reset(); } + private: base::MessageLoop* loop_; - ui::MockTouchEventConverterEvdev* device_; + scoped_ptr<ui::MockTouchEventConverterEvdev> device_; scoped_ptr<ui::MockDeviceEventDispatcherEvdev> dispatcher_; int events_out_; @@ -501,6 +503,44 @@ EXPECT_FLOAT_EQ(0.50196081f, ev.pressure); } +TEST_F(TouchEventConverterEvdevTest, ShouldReleaseContactsOnStop) { + ui::MockTouchEventConverterEvdev* dev = device(); + + InitPixelTouchscreen(dev); + + // Captured from Chromebook Pixel (Link). + timeval time; + time = {1429651083, 686882}; + struct input_event mock_kernel_queue_press[] = { + {time, EV_ABS, ABS_MT_TRACKING_ID, 0}, + {time, EV_ABS, ABS_MT_POSITION_X, 1003}, + {time, EV_ABS, ABS_MT_POSITION_Y, 749}, + {time, EV_ABS, ABS_MT_PRESSURE, 50}, + {time, EV_ABS, ABS_MT_TOUCH_MAJOR, 116}, + {time, EV_KEY, BTN_TOUCH, 1}, + {time, EV_ABS, ABS_X, 1003}, + {time, EV_ABS, ABS_Y, 749}, + {time, EV_ABS, ABS_PRESSURE, 50}, + {time, EV_SYN, SYN_REPORT, 0}, + }; + + dev->ConfigureReadMock(mock_kernel_queue_press, + arraysize(mock_kernel_queue_press), 0); + dev->ReadNow(); + EXPECT_EQ(1u, size()); + + ui::TouchEventParams ev1 = dispatched_event(0); + EXPECT_EQ(ET_TOUCH_PRESSED, ev1.type); + EXPECT_EQ(0, ev1.slot); + + DestroyDevice(); + EXPECT_EQ(2u, size()); + + ui::TouchEventParams ev2 = dispatched_event(1); + EXPECT_EQ(ET_TOUCH_RELEASED, ev2.type); + EXPECT_EQ(0, ev2.slot); +} + // crbug.com/407386 TEST_F(TouchEventConverterEvdevTest, DontChangeMultitouchPositionFromLegacyAxes) {
diff --git a/ui/events/ozone/layout/layout_util.cc b/ui/events/ozone/layout/layout_util.cc index b36bf57..e19c22d 100644 --- a/ui/events/ozone/layout/layout_util.cc +++ b/ui/events/ozone/layout/layout_util.cc
@@ -4,542 +4,11 @@ #include "ui/events/ozone/layout/layout_util.h" -#include <algorithm> - #include "ui/events/event_constants.h" -#include "ui/events/keycodes/dom3/dom_code.h" #include "ui/events/keycodes/dom3/dom_key.h" -#include "ui/events/keycodes/keyboard_code_conversion.h" namespace ui { -namespace { - -// This table, used by DomKeyToKeyboardCode(), maps DOM Level 3 .code -// values to legacy Windows-based VKEY values, where the VKEYs are -// interpreted positionally. -const struct DomCodeToKeyboardCodeEntry { - DomCode dom_code; - KeyboardCode key_code; -} dom_code_to_keyboard_code[] = { - // Entries are ordered by numeric value of the DomCode enum, - // which is the USB physical key code. - // DomCode::HYPER 0x000010 Hyper - // DomCode::SUPER 0x000011 Super - // DomCode::FN 0x000012 Fn - // DomCode::FN_LOCK 0x000013 FLock - // DomCode::SUSPEND 0x000014 Suspend - // DomCode::RESUME 0x000015 Resume - // DomCode::TURBO 0x000016 Turbo - {DomCode::SLEEP, VKEY_SLEEP}, // 0x010082 Sleep - // DomCode::WAKE_UP 0x010083 WakeUp - {DomCode::KEY_A, VKEY_A}, // 0x070004 KeyA - {DomCode::KEY_B, VKEY_B}, // 0x070005 KeyB - {DomCode::KEY_C, VKEY_C}, // 0x070006 KeyC - {DomCode::KEY_D, VKEY_D}, // 0x070007 KeyD - {DomCode::KEY_E, VKEY_E}, // 0x070008 KeyE - {DomCode::KEY_F, VKEY_F}, // 0x070009 KeyF - {DomCode::KEY_G, VKEY_G}, // 0x07000A KeyG - {DomCode::KEY_H, VKEY_H}, // 0x07000B KeyH - {DomCode::KEY_I, VKEY_I}, // 0x07000C KeyI - {DomCode::KEY_J, VKEY_J}, // 0x07000D KeyJ - {DomCode::KEY_K, VKEY_K}, // 0x07000E KeyK - {DomCode::KEY_L, VKEY_L}, // 0x07000F KeyL - {DomCode::KEY_M, VKEY_M}, // 0x070010 KeyM - {DomCode::KEY_N, VKEY_N}, // 0x070011 KeyN - {DomCode::KEY_O, VKEY_O}, // 0x070012 KeyO - {DomCode::KEY_P, VKEY_P}, // 0x070013 KeyP - {DomCode::KEY_Q, VKEY_Q}, // 0x070014 KeyQ - {DomCode::KEY_R, VKEY_R}, // 0x070015 KeyR - {DomCode::KEY_S, VKEY_S}, // 0x070016 KeyS - {DomCode::KEY_T, VKEY_T}, // 0x070017 KeyT - {DomCode::KEY_U, VKEY_U}, // 0x070018 KeyU - {DomCode::KEY_V, VKEY_V}, // 0x070019 KeyV - {DomCode::KEY_W, VKEY_W}, // 0x07001A KeyW - {DomCode::KEY_X, VKEY_X}, // 0x07001B KeyX - {DomCode::KEY_Y, VKEY_Y}, // 0x07001C KeyY - {DomCode::KEY_Z, VKEY_Z}, // 0x07001D KeyZ - {DomCode::DIGIT1, VKEY_1}, // 0x07001E Digit1 - {DomCode::DIGIT2, VKEY_2}, // 0x07001F Digit2 - {DomCode::DIGIT3, VKEY_3}, // 0x070020 Digit3 - {DomCode::DIGIT4, VKEY_4}, // 0x070021 Digit4 - {DomCode::DIGIT5, VKEY_5}, // 0x070022 Digit5 - {DomCode::DIGIT6, VKEY_6}, // 0x070023 Digit6 - {DomCode::DIGIT7, VKEY_7}, // 0x070024 Digit7 - {DomCode::DIGIT8, VKEY_8}, // 0x070025 Digit8 - {DomCode::DIGIT9, VKEY_9}, // 0x070026 Digit9 - {DomCode::DIGIT0, VKEY_0}, // 0x070027 Digit0 - {DomCode::ENTER, VKEY_RETURN}, // 0x070028 Enter - {DomCode::ESCAPE, VKEY_ESCAPE}, // 0x070029 Escape - {DomCode::BACKSPACE, VKEY_BACK}, // 0x07002A Backspace - {DomCode::TAB, VKEY_TAB}, // 0x07002B Tab - {DomCode::SPACE, VKEY_SPACE}, // 0x07002C Space - {DomCode::MINUS, VKEY_OEM_MINUS}, // 0x07002D Minus - {DomCode::EQUAL, VKEY_OEM_PLUS}, // 0x07002E Equal - {DomCode::BRACKET_LEFT, VKEY_OEM_4}, // 0x07002F BracketLeft - {DomCode::BRACKET_RIGHT, VKEY_OEM_6}, // 0x070030 BracketRight - {DomCode::BACKSLASH, VKEY_OEM_5}, // 0x070031 Backslash - // DomCode::INTL_HASH, VKEY_OEM_5 // 0x070032 IntlHash - {DomCode::SEMICOLON, VKEY_OEM_1}, // 0x070033 Semicolon - {DomCode::QUOTE, VKEY_OEM_7}, // 0x070034 Quote - {DomCode::BACKQUOTE, VKEY_OEM_3}, // 0x070035 Backquote - {DomCode::COMMA, VKEY_OEM_COMMA}, // 0x070036 Comma - {DomCode::PERIOD, VKEY_OEM_PERIOD}, // 0x070037 Period - {DomCode::SLASH, VKEY_OEM_2}, // 0x070038 Slash - {DomCode::CAPS_LOCK, VKEY_CAPITAL}, // 0x070039 CapsLock - {DomCode::F1, VKEY_F1}, // 0x07003A F1 - {DomCode::F2, VKEY_F2}, // 0x07003B F2 - {DomCode::F3, VKEY_F3}, // 0x07003C F3 - {DomCode::F4, VKEY_F4}, // 0x07003D F4 - {DomCode::F5, VKEY_F5}, // 0x07003E F5 - {DomCode::F6, VKEY_F6}, // 0x07003F F6 - {DomCode::F7, VKEY_F7}, // 0x070040 F7 - {DomCode::F8, VKEY_F8}, // 0x070041 F8 - {DomCode::F9, VKEY_F9}, // 0x070042 F9 - {DomCode::F10, VKEY_F10}, // 0x070043 F10 - {DomCode::F11, VKEY_F11}, // 0x070044 F11 - {DomCode::F12, VKEY_F12}, // 0x070045 F12 - {DomCode::PRINT_SCREEN, VKEY_SNAPSHOT}, // 0x070046 PrintScreen - {DomCode::SCROLL_LOCK, VKEY_SCROLL}, // 0x070047 ScrollLock - {DomCode::PAUSE, VKEY_PAUSE}, // 0x070048 Pause - {DomCode::INSERT, VKEY_INSERT}, // 0x070049 Insert - {DomCode::HOME, VKEY_HOME}, // 0x07004A Home - {DomCode::PAGE_UP, VKEY_PRIOR}, // 0x07004B PageUp - {DomCode::DEL, VKEY_DELETE}, // 0x07004C Delete - {DomCode::END, VKEY_END}, // 0x07004D End - {DomCode::PAGE_DOWN, VKEY_NEXT}, // 0x07004E PageDown - {DomCode::ARROW_RIGHT, VKEY_RIGHT}, // 0x07004F ArrowRight - {DomCode::ARROW_LEFT, VKEY_LEFT}, // 0x070050 ArrowLeft - {DomCode::ARROW_DOWN, VKEY_DOWN}, // 0x070051 ArrowDown - {DomCode::ARROW_UP, VKEY_UP}, // 0x070052 ArrowUp - {DomCode::NUM_LOCK, VKEY_NUMLOCK}, // 0x070053 NumLock - {DomCode::NUMPAD_DIVIDE, VKEY_DIVIDE}, // 0x070054 NumpadDivide - {DomCode::NUMPAD_MULTIPLY, VKEY_MULTIPLY}, // 0x070055 NumpadMultiply - {DomCode::NUMPAD_SUBTRACT, VKEY_SUBTRACT}, // 0x070056 NumpadSubtract - {DomCode::NUMPAD_ADD, VKEY_ADD}, // 0x070057 NumpadAdd - {DomCode::NUMPAD_ENTER, VKEY_RETURN}, // 0x070058 NumpadEnter - {DomCode::NUMPAD1, VKEY_NUMPAD1}, // 0x070059 Numpad1 - {DomCode::NUMPAD2, VKEY_NUMPAD2}, // 0x07005A Numpad2 - {DomCode::NUMPAD3, VKEY_NUMPAD3}, // 0x07005B Numpad3 - {DomCode::NUMPAD4, VKEY_NUMPAD4}, // 0x07005C Numpad4 - {DomCode::NUMPAD5, VKEY_NUMPAD5}, // 0x07005D Numpad5 - {DomCode::NUMPAD6, VKEY_NUMPAD6}, // 0x07005E Numpad6 - {DomCode::NUMPAD7, VKEY_NUMPAD7}, // 0x07005F Numpad7 - {DomCode::NUMPAD8, VKEY_NUMPAD8}, // 0x070060 Numpad8 - {DomCode::NUMPAD9, VKEY_NUMPAD9}, // 0x070061 Numpad9 - {DomCode::NUMPAD0, VKEY_NUMPAD0}, // 0x070062 Numpad0 - {DomCode::NUMPAD_DECIMAL, VKEY_DECIMAL}, // 0x070063 NumpadDecimal - {DomCode::INTL_BACKSLASH, VKEY_OEM_102}, // 0x070064 IntlBackslash - {DomCode::CONTEXT_MENU, VKEY_APPS}, // 0x070065 ContextMenu - {DomCode::POWER, VKEY_POWER}, // 0x070066 Power - // DomCode::NUMPAD_EQUAL 0x070067 NumpadEqual - // DomCode::OPEN 0x070074 Open - {DomCode::HELP, VKEY_HELP}, // 0x070075 Help - {DomCode::SELECT, VKEY_SELECT}, // 0x070077 Select - // DomCode::AGAIN 0x070079 Again - // DomCode::UNDO 0x07007A Undo - // DomCode::CUT 0x07007B Cut - // DomCode::COPY 0x07007C Copy - // DomCode::PASTE 0x07007D Paste - // DomCode::FIND 0x07007E Find - {DomCode::VOLUME_MUTE, VKEY_VOLUME_MUTE}, // 0x07007F VolumeMute - {DomCode::VOLUME_UP, VKEY_VOLUME_UP}, // 0x070080 VolumeUp - {DomCode::VOLUME_DOWN, VKEY_VOLUME_DOWN}, // 0x070081 VolumeDown - {DomCode::NUMPAD_COMMA, VKEY_OEM_COMMA}, // 0x070085 NumpadComma - {DomCode::INTL_RO, VKEY_OEM_102}, // 0x070087 IntlRo - {DomCode::KANA_MODE, VKEY_KANA}, // 0x070088 KanaMode - {DomCode::INTL_YEN, VKEY_OEM_5}, // 0x070089 IntlYen - {DomCode::CONVERT, VKEY_CONVERT}, // 0x07008A Convert - {DomCode::NON_CONVERT, VKEY_NONCONVERT}, // 0x07008B NonConvert - {DomCode::LANG1, VKEY_KANA}, // 0x070090 Lang1 - {DomCode::LANG2, VKEY_KANJI}, // 0x070091 Lang2 - // DomCode::LANG3 0x070092 Lang3 - // DomCode::LANG4 0x070093 Lang4 - // DomCode::LANG5 0x070094 Lang5 - // DomCode::ABORT 0x07009B Abort - // DomCode::PROPS 0x0700A3 Props - // DomCode::NUMPAD_PAREN_LEFT 0x0700B6 NumpadParenLeft - // DomCode::NUMPAD_PAREN_RIGHT 0x0700B7 NumpadParenRight - {DomCode::NUMPAD_BACKSPACE, VKEY_BACK}, // 0x0700BB NumpadBackspace - // DomCode::NUMPAD_MEMORY_STORE 0x0700D0 NumpadMemoryStore - // DomCode::NUMPAD_MEMORY_RECALL 0x0700D1 NumpadMemoryRecall - // DomCode::NUMPAD_MEMORY_CLEAR 0x0700D2 NumpadMemoryClear - // DomCode::NUMPAD_MEMORY_ADD 0x0700D3 NumpadMemoryAdd - // DomCode::NUMPAD_MEMORY_SUBTRACT 0x0700D4 - // NumpadMemorySubtract - {DomCode::NUMPAD_CLEAR, VKEY_CLEAR}, // 0x0700D8 NumpadClear - {DomCode::NUMPAD_CLEAR_ENTRY, VKEY_CLEAR}, // 0x0700D9 NumpadClearEntry - {DomCode::CONTROL_LEFT, VKEY_LCONTROL}, // 0x0700E0 ControlLeft - {DomCode::SHIFT_LEFT, VKEY_LSHIFT}, // 0x0700E1 ShiftLeft - {DomCode::ALT_LEFT, VKEY_LMENU}, // 0x0700E2 AltLeft - {DomCode::OS_LEFT, VKEY_LWIN}, // 0x0700E3 OSLeft - {DomCode::CONTROL_RIGHT, VKEY_RCONTROL}, // 0x0700E4 ControlRight - {DomCode::SHIFT_RIGHT, VKEY_RSHIFT}, // 0x0700E5 ShiftRight - {DomCode::ALT_RIGHT, VKEY_RMENU}, // 0x0700E6 AltRight - {DomCode::OS_RIGHT, VKEY_RWIN}, // 0x0700E7 OSRight - {DomCode::MEDIA_TRACK_NEXT, - VKEY_MEDIA_NEXT_TRACK}, // 0x0C00B5 MediaTrackNext - {DomCode::MEDIA_TRACK_PREVIOUS, - VKEY_MEDIA_PREV_TRACK}, // 0x0C00B6 MediaTrackPrevious - {DomCode::MEDIA_STOP, VKEY_MEDIA_STOP}, // 0x0C00B7 MediaStop - // DomCode::EJECT 0x0C00B8 Eject - {DomCode::MEDIA_PLAY_PAUSE, - VKEY_MEDIA_PLAY_PAUSE}, // 0x0C00CD MediaPlayPause - {DomCode::MEDIA_SELECT, - VKEY_MEDIA_LAUNCH_MEDIA_SELECT}, // 0x0C0183 MediaSelect - {DomCode::LAUNCH_MAIL, VKEY_MEDIA_LAUNCH_MAIL}, // 0x0C018A LaunchMail - {DomCode::LAUNCH_APP2, VKEY_MEDIA_LAUNCH_APP2}, // 0x0C0192 LaunchApp2 - {DomCode::LAUNCH_APP1, VKEY_MEDIA_LAUNCH_APP1}, // 0x0C0194 LaunchApp1 - {DomCode::BROWSER_SEARCH, VKEY_BROWSER_SEARCH}, // 0x0C0221 BrowserSearch - {DomCode::BROWSER_HOME, VKEY_BROWSER_HOME}, // 0x0C0223 BrowserHome - {DomCode::BROWSER_BACK, VKEY_BROWSER_BACK}, // 0x0C0224 BrowserBack - {DomCode::BROWSER_FORWARD, - VKEY_BROWSER_FORWARD}, // 0x0C0225 BrowserForward - {DomCode::BROWSER_STOP, VKEY_BROWSER_STOP}, // 0x0C0226 BrowserStop - {DomCode::BROWSER_REFRESH, - VKEY_BROWSER_REFRESH}, // 0x0C0227 BrowserRefresh - {DomCode::BROWSER_FAVORITES, - VKEY_BROWSER_FAVORITES}, // 0x0C022A BrowserFavorites -}; - -} // anonymous namespace - -// Returns a Windows-based VKEY for a non-printable DOM Level 3 |key|. -// The returned VKEY is non-positional (e.g. VKEY_SHIFT). -KeyboardCode NonPrintableDomKeyToKeyboardCode(DomKey dom_key) { - switch (dom_key) { - // No value. - case DomKey::NONE: - return VKEY_UNKNOWN; - // Character values. - // Special Key Values - // http://www.w3.org/TR/DOM-Level-3-Events-key/#keys-special - case DomKey::UNIDENTIFIED: - return VKEY_UNKNOWN; - // Modifier Keys - // http://www.w3.org/TR/DOM-Level-3-Events-key/#keys-modifier - case DomKey::ALT: - return VKEY_MENU; - case DomKey::ALT_GRAPH: - return VKEY_ALTGR; - case DomKey::CAPS_LOCK: - return VKEY_CAPITAL; - case DomKey::CONTROL: - return VKEY_CONTROL; - case DomKey::NUM_LOCK: - return VKEY_NUMLOCK; - case DomKey::OS: - return VKEY_LWIN; - case DomKey::SCROLL_LOCK: - return VKEY_SCROLL; - case DomKey::SHIFT: - return VKEY_SHIFT; - // Whitespace Keys - // http://www.w3.org/TR/DOM-Level-3-Events-key/#keys-whitespace - case DomKey::ENTER: - return VKEY_RETURN; - case DomKey::SEPARATOR: - return VKEY_SEPARATOR; - case DomKey::TAB: - return VKEY_TAB; - // Navigation Keys - // http://www.w3.org/TR/DOM-Level-3-Events-key/#keys-navigation - case DomKey::ARROW_DOWN: - return VKEY_DOWN; - case DomKey::ARROW_LEFT: - return VKEY_LEFT; - case DomKey::ARROW_RIGHT: - return VKEY_RIGHT; - case DomKey::ARROW_UP: - return VKEY_UP; - case DomKey::END: - return VKEY_END; - case DomKey::HOME: - return VKEY_HOME; - case DomKey::PAGE_DOWN: - return VKEY_NEXT; - case DomKey::PAGE_UP: - return VKEY_PRIOR; - // Editing Keys - // http://www.w3.org/TR/DOM-Level-3-Events-key/#keys-editing - case DomKey::BACKSPACE: - return VKEY_BACK; - case DomKey::CLEAR: - return VKEY_CLEAR; - case DomKey::CR_SEL: - return VKEY_CRSEL; - case DomKey::DEL: - return VKEY_DELETE; - case DomKey::ERASE_EOF: - return VKEY_EREOF; - case DomKey::EX_SEL: - return VKEY_EXSEL; - case DomKey::INSERT: - return VKEY_INSERT; - // UI Keys - // http://www.w3.org/TR/DOM-Level-3-Events-key/#keys-ui - case DomKey::ACCEPT: - return VKEY_ACCEPT; - case DomKey::ATTN: - return VKEY_ATTN; - case DomKey::CONTEXT_MENU: - return VKEY_APPS; - case DomKey::ESCAPE: - return VKEY_ESCAPE; - case DomKey::EXECUTE: - return VKEY_EXECUTE; - case DomKey::HELP: - return VKEY_HELP; - case DomKey::PAUSE: - return VKEY_PAUSE; - case DomKey::PLAY: - return VKEY_PLAY; - case DomKey::SELECT: - return VKEY_SELECT; - // Device Keys - // http://www.w3.org/TR/DOM-Level-3-Events-key/#keys-device - case DomKey::BRIGHTNESS_DOWN: - return VKEY_BRIGHTNESS_DOWN; - case DomKey::BRIGHTNESS_UP: - return VKEY_BRIGHTNESS_UP; - case DomKey::POWER: - return VKEY_POWER; - case DomKey::PRINT_SCREEN: - return VKEY_SNAPSHOT; -// IME and Composition Keys -// http://www.w3.org/TR/DOM-Level-3-Events-key/#keys-composition -#if 0 // TODO(kpschoedel) - case DomKey::COMPOSE: - return VKEY_COMPOSE; -#endif - case DomKey::CONVERT: - return VKEY_CONVERT; - case DomKey::FINAL_MODE: - return VKEY_FINAL; - case DomKey::MODE_CHANGE: - return VKEY_MODECHANGE; - case DomKey::NON_CONVERT: - return VKEY_NONCONVERT; - case DomKey::PROCESS: - return VKEY_PROCESSKEY; - // Keys specific to Korean keyboards - case DomKey::HANGUL_MODE: - return VKEY_HANGUL; - case DomKey::HANJA_MODE: - return VKEY_HANJA; - case DomKey::JUNJA_MODE: - return VKEY_JUNJA; - // Keys specific to Japanese keyboards - case DomKey::HANKAKU: - return VKEY_DBE_SBCSCHAR; - case DomKey::KANA_MODE: - return VKEY_KANA; - case DomKey::KANJI_MODE: - return VKEY_KANJI; - case DomKey::ZENKAKU: - return VKEY_DBE_DBCSCHAR; - case DomKey::ZENKAKU_HANKAKU: - return VKEY_DBE_DBCSCHAR; - // General-Purpose Function Keys - // http://www.w3.org/TR/DOM-Level-3-Events-key/#keys-function - case DomKey::F1: - return VKEY_F1; - case DomKey::F2: - return VKEY_F2; - case DomKey::F3: - return VKEY_F3; - case DomKey::F4: - return VKEY_F4; - case DomKey::F5: - return VKEY_F5; - case DomKey::F6: - return VKEY_F6; - case DomKey::F7: - return VKEY_F7; - case DomKey::F8: - return VKEY_F8; - case DomKey::F9: - return VKEY_F9; - case DomKey::F10: - return VKEY_F10; - case DomKey::F11: - return VKEY_F11; - case DomKey::F12: - return VKEY_F12; - case DomKey::F13: - return VKEY_F13; - case DomKey::F14: - return VKEY_F14; - case DomKey::F15: - return VKEY_F15; - case DomKey::F16: - return VKEY_F16; - case DomKey::F17: - return VKEY_F17; - case DomKey::F18: - return VKEY_F18; - case DomKey::F19: - return VKEY_F19; - case DomKey::F20: - return VKEY_F20; - case DomKey::F21: - return VKEY_F21; - case DomKey::F22: - return VKEY_F22; - case DomKey::F23: - return VKEY_F23; - case DomKey::F24: - return VKEY_F24; - // Multimedia Keys - // http://www.w3.org/TR/DOM-Level-3-Events-key/#keys-multimedia - case DomKey::MEDIA_PLAY_PAUSE: - return VKEY_MEDIA_PLAY_PAUSE; - case DomKey::MEDIA_SELECT: - return VKEY_MEDIA_LAUNCH_MEDIA_SELECT; - case DomKey::MEDIA_STOP: - return VKEY_MEDIA_STOP; - case DomKey::MEDIA_TRACK_NEXT: - return VKEY_MEDIA_NEXT_TRACK; - case DomKey::MEDIA_TRACK_PREVIOUS: - return VKEY_MEDIA_PREV_TRACK; - case DomKey::PRINT: - return VKEY_PRINT; - case DomKey::VOLUME_DOWN: - return VKEY_VOLUME_DOWN; - case DomKey::VOLUME_MUTE: - return VKEY_VOLUME_MUTE; - case DomKey::VOLUME_UP: - return VKEY_VOLUME_UP; - // Application Keys - // http://www.w3.org/TR/DOM-Level-3-Events-key/#keys-apps - case DomKey::LAUNCH_CALCULATOR: - return VKEY_MEDIA_LAUNCH_APP2; - case DomKey::LAUNCH_MAIL: - return VKEY_MEDIA_LAUNCH_MAIL; - case DomKey::LAUNCH_MY_COMPUTER: - return VKEY_MEDIA_LAUNCH_APP1; - // Browser Keys - // http://www.w3.org/TR/DOM-Level-3-Events-key/#keys-browser - case DomKey::BROWSER_BACK: - return VKEY_BROWSER_BACK; - case DomKey::BROWSER_FAVORITES: - return VKEY_BROWSER_FAVORITES; - case DomKey::BROWSER_FORWARD: - return VKEY_BROWSER_FORWARD; - case DomKey::BROWSER_HOME: - return VKEY_BROWSER_HOME; - case DomKey::BROWSER_REFRESH: - return VKEY_BROWSER_REFRESH; - case DomKey::BROWSER_SEARCH: - return VKEY_BROWSER_SEARCH; - case DomKey::BROWSER_STOP: - return VKEY_BROWSER_STOP; - // Media Controller Keys - // http://www.w3.org/TR/DOM-Level-3-Events-key/#keys-media-controller - case DomKey::ZOOM_TOGGLE: - return VKEY_ZOOM; - // No value. - default: - return VKEY_UNKNOWN; - } -} - -// Returns the Windows-based VKEY value corresponding to a DOM Level 3 |code|. -// The returned VKEY is located (e.g. VKEY_LSHIFT). -KeyboardCode DomCodeToKeyboardCode(DomCode dom_code) { - const DomCodeToKeyboardCodeEntry* end = - dom_code_to_keyboard_code + arraysize(dom_code_to_keyboard_code); - const DomCodeToKeyboardCodeEntry* found = - std::lower_bound(dom_code_to_keyboard_code, end, dom_code, - [](const DomCodeToKeyboardCodeEntry& a, DomCode b) { - return static_cast<int>(a.dom_code) < static_cast<int>(b); - }); - if ((found != end) && (found->dom_code == dom_code)) - return found->key_code; - return VKEY_UNKNOWN; -} - -// Returns the Windows-based VKEY value corresponding to a DOM Level 3 |code|. -// The returned VKEY is non-located (e.g. VKEY_SHIFT). -KeyboardCode DomCodeToNonLocatedKeyboardCode(DomCode dom_code) { - return LocatedToNonLocatedKeyboardCode(DomCodeToKeyboardCode(dom_code)); -} - -bool LookupControlCharacter(DomCode dom_code, - int flags, - DomKey* dom_key, - base::char16* character, - KeyboardCode* key_code) { - if ((flags & EF_CONTROL_DOWN) == 0) - return false; - - int code = static_cast<int>(dom_code); - const int kKeyA = static_cast<int>(DomCode::KEY_A); - // Control-A - Control-Z map to 0x01 - 0x1A. - if (code >= kKeyA && code <= static_cast<int>(DomCode::KEY_Z)) { - *character = static_cast<base::char16>(code - kKeyA + 1); - *key_code = static_cast<KeyboardCode>(code - kKeyA + VKEY_A); - switch (dom_code) { - case DomCode::KEY_H: - *dom_key = DomKey::BACKSPACE; - case DomCode::KEY_I: - *dom_key = DomKey::TAB; - case DomCode::KEY_M: - *dom_key = DomKey::ENTER; - default: - *dom_key = DomKey::CHARACTER; - } - return true; - } - - switch (dom_code) { - case DomCode::DIGIT2: - // NUL - *character = 0; - *dom_key = DomKey::CHARACTER; - *key_code = VKEY_2; - return true; - case DomCode::ENTER: - // NL - *character = 0x0A; - *dom_key = DomKey::CHARACTER; - *key_code = VKEY_RETURN; - return true; - case DomCode::BRACKET_LEFT: - // ESC - *character = 0x1B; - *dom_key = DomKey::ESCAPE; - *key_code = VKEY_OEM_4; - return true; - case DomCode::BACKSLASH: - // FS - *character = 0x1C; - *dom_key = DomKey::CHARACTER; - *key_code = VKEY_OEM_5; - return true; - case DomCode::BRACKET_RIGHT: - // GS - *character = 0x1D; - *dom_key = DomKey::CHARACTER; - *key_code = VKEY_OEM_6; - return true; - case DomCode::DIGIT6: - // RS - *character = 0x1E; - *dom_key = DomKey::CHARACTER; - *key_code = VKEY_6; - return true; - case DomCode::MINUS: - // US - *character = 0x1F; - *dom_key = DomKey::CHARACTER; - *key_code = VKEY_OEM_MINUS; - return true; - default: - return false; - } -} - int ModifierDomKeyToEventFlag(DomKey key) { switch (key) { case DomKey::ALT:
diff --git a/ui/events/ozone/layout/layout_util.h b/ui/events/ozone/layout/layout_util.h index 7eb8f779..84d9d1d 100644 --- a/ui/events/ozone/layout/layout_util.h +++ b/ui/events/ozone/layout/layout_util.h
@@ -13,31 +13,8 @@ namespace ui { -enum class DomCode; enum class DomKey; -// Returns a Windows-based VKEY for a non-printable DOM Level 3 |key|. -// The returned VKEY is non-located (e.g. VKEY_SHIFT). -EVENTS_OZONE_LAYOUT_EXPORT KeyboardCode -NonPrintableDomKeyToKeyboardCode(DomKey dom_key); - -// Returns the Windows-based VKEY value corresponding to a DOM Level 3 |code|. -// The returned VKEY is located (e.g. VKEY_LSHIFT). -EVENTS_OZONE_LAYOUT_EXPORT KeyboardCode DomCodeToKeyboardCode(DomCode dom_code); - -// Returns the Windows-based VKEY value corresponding to a DOM Level 3 |code|. -// The returned VKEY is non-located (e.g. VKEY_SHIFT). -EVENTS_OZONE_LAYOUT_EXPORT KeyboardCode -DomCodeToNonLocatedKeyboardCode(DomCode dom_code); - -// Returns true control character corresponding to a physical key. -// In some contexts this is used instead of the key layout. -EVENTS_OZONE_LAYOUT_EXPORT bool LookupControlCharacter(DomCode dom_code, - int flags, - DomKey* dom_key, - base::char16* character, - KeyboardCode* key_code); - // Returns the ui::EventFlags value associated with a modifier key, // or 0 (EF_NONE) if the key is not a modifier. EVENTS_OZONE_LAYOUT_EXPORT int ModifierDomKeyToEventFlag(DomKey key);
diff --git a/ui/events/ozone/layout/stub/stub_keyboard_layout_engine.cc b/ui/events/ozone/layout/stub/stub_keyboard_layout_engine.cc index 4279901..9a522df6 100644 --- a/ui/events/ozone/layout/stub/stub_keyboard_layout_engine.cc +++ b/ui/events/ozone/layout/stub/stub_keyboard_layout_engine.cc
@@ -15,221 +15,6 @@ namespace ui { -namespace { - -// All of the characters have low ordinals, so we use bit 15 to flag dead keys. -#define DK 0x8000 - -const struct PrintableCodeEntry { - DomCode dom_code; - base::char16 character[4]; -} printable_code_map[] = { - // Stub table based on X US international. - {DomCode::KEY_A, {'a', 'A', 0x00E1, 0x00C1}}, - {DomCode::KEY_B, {'b', 'B', 'b', 'B'}}, - {DomCode::KEY_C, {'c', 'C', 0x00A9, 0x00A2}}, - {DomCode::KEY_D, {'d', 'D', 0x00F0, 0x00D0}}, - {DomCode::KEY_E, {'e', 'E', 0x00E9, 0x00C9}}, - {DomCode::KEY_F, {'f', 'F', 'f', 'F'}}, - {DomCode::KEY_G, {'g', 'G', 'g', 'G'}}, - {DomCode::KEY_H, {'h', 'H', 'h', 'H'}}, - {DomCode::KEY_I, {'i', 'I', 0x00ED, 0x00CD}}, - {DomCode::KEY_J, {'j', 'J', 'j', 'J'}}, - {DomCode::KEY_K, {'k', 'K', 0x0153, 0x0152}}, - {DomCode::KEY_L, {'l', 'L', 0x00F8, 0x00D8}}, - {DomCode::KEY_M, {'m', 'M', 0x00B5, 0x00B5}}, - {DomCode::KEY_N, {'n', 'N', 0x00F1, 0x00D1}}, - {DomCode::KEY_O, {'o', 'O', 0x00F3, 0x00D3}}, - {DomCode::KEY_P, {'p', 'P', 0x00F6, 0x00D6}}, - {DomCode::KEY_Q, {'q', 'Q', 0x00E4, 0x00C4}}, - {DomCode::KEY_R, {'r', 'R', 0x00AE, 0x00AE}}, - {DomCode::KEY_S, {'s', 'S', 0x00DF, 0x00A7}}, - {DomCode::KEY_T, {'t', 'T', 0x00FE, 0x00DE}}, - {DomCode::KEY_U, {'u', 'U', 0x00FA, 0x00DA}}, - {DomCode::KEY_V, {'v', 'V', 'v', 'V'}}, - {DomCode::KEY_W, {'w', 'W', 0x00E5, 0x00C5}}, - {DomCode::KEY_X, {'x', 'X', 'x', 'X'}}, - {DomCode::KEY_Y, {'y', 'Y', 0x00FC, 0x00DC}}, - {DomCode::KEY_Z, {'z', 'Z', 0x00E6, 0x00C6}}, - {DomCode::DIGIT1, {'1', '!', 0x00A1, 0x00B9}}, - {DomCode::DIGIT2, {'2', '@', 0x00B2, DK|0x030B}}, - {DomCode::DIGIT3, {'3', '#', 0x00B3, DK|0x0304}}, - {DomCode::DIGIT4, {'4', '$', 0x00A4, 0x00A3}}, - {DomCode::DIGIT5, {'5', '%', 0x20AC, DK|0x0327}}, - {DomCode::DIGIT6, {'6', '^', DK|0x0302, 0x00BC}}, - {DomCode::DIGIT7, {'7', '&', 0x00BD, DK|0x031B}}, - {DomCode::DIGIT8, {'8', '*', 0x00BE, DK|0x0328}}, - {DomCode::DIGIT9, {'9', '(', 0x2018, DK|0x0306}}, - {DomCode::DIGIT0, {'0', ')', 0x2019, DK|0x030A}}, - {DomCode::SPACE, {' ', ' ', 0x00A0, 0x00A0}}, - {DomCode::MINUS, {'-', '_', 0x00A5, DK|0x0323}}, - {DomCode::EQUAL, {'=', '+', 0x00D7, 0x00F7}}, - {DomCode::BRACKET_LEFT, {'[', '{', 0x00AB, 0x201C}}, - {DomCode::BRACKET_RIGHT, {']', '}', 0x00BB, 0x201D}}, - {DomCode::BACKSLASH, {'\\', '|', 0x00AC, 0x00A6}}, - {DomCode::SEMICOLON, {';', ':', 0x00B6, 0x00B0}}, - {DomCode::QUOTE, {'\'', '"', DK|0x0301, DK|0x0308}}, - {DomCode::BACKQUOTE, {'`', '~', DK|0x0300, DK|0x0303}}, - {DomCode::COMMA, {',', '<', 0x00E7, 0x00C7}}, - {DomCode::PERIOD, {'.', '>', DK|0x0307, DK|0x030C}}, - {DomCode::SLASH, {'/', '?', 0x00BF, DK|0x0309}}, - {DomCode::INTL_BACKSLASH, {'\\', '|', '\\', '|'}}, - {DomCode::INTL_YEN, {0x00A5, '|', 0x00A5, '|'}}, - {DomCode::NUMPAD_DIVIDE, {'/', '/', '/', '/'}}, - {DomCode::NUMPAD_MULTIPLY, {'*', '*', '*', '*'}}, - {DomCode::NUMPAD_SUBTRACT, {'-', '-', '-', '-'}}, - {DomCode::NUMPAD_ADD, {'+', '+', '+', '+'}}, - {DomCode::NUMPAD1, {'1', '1', '1', '1'}}, - {DomCode::NUMPAD2, {'2', '2', '2', '2'}}, - {DomCode::NUMPAD3, {'3', '3', '3', '3'}}, - {DomCode::NUMPAD4, {'4', '4', '4', '4'}}, - {DomCode::NUMPAD5, {'5', '5', '5', '5'}}, - {DomCode::NUMPAD6, {'6', '6', '6', '6'}}, - {DomCode::NUMPAD7, {'7', '7', '7', '7'}}, - {DomCode::NUMPAD8, {'8', '8', '8', '8'}}, - {DomCode::NUMPAD9, {'9', '9', '9', '9'}}, - {DomCode::NUMPAD0, {'0', '0', '0', '0'}}, - {DomCode::NUMPAD_DECIMAL, {'.', '.', '.', '.'}}, - {DomCode::NUMPAD_EQUAL, {'=', '=', '=', '='}}, - {DomCode::NUMPAD_COMMA, {',', ',', ',', ','}}, - {DomCode::NUMPAD_PAREN_LEFT, {'(', '(', '(', '('}}, - {DomCode::NUMPAD_PAREN_RIGHT, {')', ')', ')', ')'}}, - {DomCode::NUMPAD_SIGN_CHANGE, {0x00B1, 0x00B1, 0x2213, 0x2213}}, -}; - -const struct NonPrintableCodeEntry { - DomCode dom_code; - DomKey dom_key; - base::char16 character; -} non_printable_code_map[] = { - {DomCode::ABORT, DomKey::CANCEL}, - {DomCode::AGAIN, DomKey::AGAIN}, - {DomCode::ALT_LEFT, DomKey::ALT}, - {DomCode::ALT_RIGHT, DomKey::ALT}, - {DomCode::ARROW_DOWN, DomKey::ARROW_DOWN}, - {DomCode::ARROW_LEFT, DomKey::ARROW_LEFT}, - {DomCode::ARROW_RIGHT, DomKey::ARROW_RIGHT}, - {DomCode::ARROW_UP, DomKey::ARROW_UP}, - {DomCode::BACKSPACE, DomKey::BACKSPACE, 0x0008}, - {DomCode::BRIGHTNESS_DOWN, DomKey::BRIGHTNESS_DOWN}, - {DomCode::BRIGHTNESS_UP, DomKey::BRIGHTNESS_UP}, - // {DomCode::BRIGHTNESS_AUTO, DomKey::_} - // {DomCode::BRIGHTNESS_MAXIMUM, DomKey::_} - // {DomCode::BRIGHTNESS_MINIMIUM, DomKey::_} - // {DomCode::BRIGHTNESS_TOGGLE, DomKey::_} - {DomCode::BROWSER_BACK, DomKey::BROWSER_BACK}, - {DomCode::BROWSER_FAVORITES, DomKey::BROWSER_FAVORITES}, - {DomCode::BROWSER_FORWARD, DomKey::BROWSER_FORWARD}, - {DomCode::BROWSER_HOME, DomKey::BROWSER_HOME}, - {DomCode::BROWSER_REFRESH, DomKey::BROWSER_REFRESH}, - {DomCode::BROWSER_SEARCH, DomKey::BROWSER_SEARCH}, - {DomCode::BROWSER_STOP, DomKey::BROWSER_STOP}, - {DomCode::CAPS_LOCK, DomKey::CAPS_LOCK}, - {DomCode::CONTEXT_MENU, DomKey::CONTEXT_MENU}, - {DomCode::CONTROL_LEFT, DomKey::CONTROL}, - {DomCode::CONTROL_RIGHT, DomKey::CONTROL}, - {DomCode::CONVERT, DomKey::CONVERT}, - {DomCode::COPY, DomKey::COPY}, - {DomCode::CUT, DomKey::CUT}, - {DomCode::DEL, DomKey::DEL, 0x007F}, - {DomCode::EJECT, DomKey::EJECT}, - {DomCode::END, DomKey::END}, - {DomCode::ENTER, DomKey::ENTER, 0x000D}, - {DomCode::ESCAPE, DomKey::ESCAPE, 0x001B}, - {DomCode::F1, DomKey::F1}, - {DomCode::F2, DomKey::F2}, - {DomCode::F3, DomKey::F3}, - {DomCode::F4, DomKey::F4}, - {DomCode::F5, DomKey::F5}, - {DomCode::F6, DomKey::F6}, - {DomCode::F7, DomKey::F7}, - {DomCode::F8, DomKey::F8}, - {DomCode::F9, DomKey::F9}, - {DomCode::F10, DomKey::F10}, - {DomCode::F11, DomKey::F11}, - {DomCode::F12, DomKey::F12}, - {DomCode::F13, DomKey::F13}, - {DomCode::F14, DomKey::F14}, - {DomCode::F15, DomKey::F15}, - {DomCode::F16, DomKey::F16}, - {DomCode::F17, DomKey::F17}, - {DomCode::F18, DomKey::F18}, - {DomCode::F19, DomKey::F19}, - {DomCode::F20, DomKey::F20}, - {DomCode::F21, DomKey::F21}, - {DomCode::F22, DomKey::F22}, - {DomCode::F23, DomKey::F23}, - {DomCode::F24, DomKey::F24}, - {DomCode::FIND, DomKey::FIND}, - {DomCode::FN, DomKey::FN}, - {DomCode::FN_LOCK, DomKey::FN_LOCK}, - {DomCode::HELP, DomKey::HELP}, - {DomCode::HOME, DomKey::HOME}, - {DomCode::HYPER, DomKey::HYPER}, - {DomCode::INSERT, DomKey::INSERT}, - // {DomCode::INTL_RO, DomKey::_} - {DomCode::KANA_MODE, DomKey::KANA_MODE}, - {DomCode::LANG1, DomKey::HANGUL_MODE}, - {DomCode::LANG2, DomKey::HANJA_MODE}, - {DomCode::LANG3, DomKey::KATAKANA}, - {DomCode::LANG4, DomKey::HIRAGANA}, - {DomCode::LANG5, DomKey::ZENKAKU_HANKAKU}, - {DomCode::LAUNCH_APP1, DomKey::LAUNCH_MY_COMPUTER}, - {DomCode::LAUNCH_APP2, DomKey::LAUNCH_CALCULATOR}, - {DomCode::LAUNCH_MAIL, DomKey::LAUNCH_MAIL}, - {DomCode::LAUNCH_SCREEN_SAVER, DomKey::LAUNCH_SCREEN_SAVER}, - // {DomCode::LAUNCH_DOCUMENTS, DomKey::_} - // {DomCode::LAUNCH_FILE_BROWSER, DomKey::_} - // {DomCode::LAUNCH_KEYBOARD_LAYOUT, DomKey::_} - {DomCode::LOCK_SCREEN, DomKey::LAUNCH_SCREEN_SAVER}, - {DomCode::MAIL_FORWARD, DomKey::MAIL_FORWARD}, - {DomCode::MAIL_REPLY, DomKey::MAIL_REPLY}, - {DomCode::MAIL_SEND, DomKey::MAIL_SEND}, - {DomCode::MEDIA_PLAY_PAUSE, DomKey::MEDIA_PLAY_PAUSE}, - {DomCode::MEDIA_SELECT, DomKey::MEDIA_SELECT}, - {DomCode::MEDIA_STOP, DomKey::MEDIA_STOP}, - {DomCode::MEDIA_TRACK_NEXT, DomKey::MEDIA_TRACK_NEXT}, - {DomCode::MEDIA_TRACK_PREVIOUS, DomKey::MEDIA_TRACK_PREVIOUS}, - // {DomCode::MENU, DomKey::_} - {DomCode::NON_CONVERT, DomKey::NON_CONVERT}, - {DomCode::NUM_LOCK, DomKey::NUM_LOCK}, - {DomCode::NUMPAD_BACKSPACE, DomKey::BACKSPACE, 0x0008}, - {DomCode::NUMPAD_CLEAR, DomKey::CLEAR}, - {DomCode::NUMPAD_ENTER, DomKey::ENTER, 0x000D}, - // {DomCode::NUMPAD_CLEAR_ENTRY, DomKey::_} - // {DomCode::NUMPAD_MEMORY_ADD, DomKey::_} - // {DomCode::NUMPAD_MEMORY_CLEAR, DomKey::_} - // {DomCode::NUMPAD_MEMORY_RECALL, DomKey::_} - // {DomCode::NUMPAD_MEMORY_STORE, DomKey::_} - // {DomCode::NUMPAD_MEMORY_SUBTRACT, DomKey::_} - {DomCode::OPEN, DomKey::OPEN}, - {DomCode::OS_LEFT, DomKey::OS}, - {DomCode::OS_RIGHT, DomKey::OS}, - {DomCode::PAGE_DOWN, DomKey::PAGE_DOWN}, - {DomCode::PAGE_UP, DomKey::PAGE_UP}, - {DomCode::PASTE, DomKey::PASTE}, - {DomCode::PAUSE, DomKey::PAUSE}, - {DomCode::POWER, DomKey::POWER}, - {DomCode::PRINT_SCREEN, DomKey::PRINT_SCREEN}, - {DomCode::PROPS, DomKey::PROPS}, - {DomCode::SCROLL_LOCK, DomKey::SCROLL_LOCK}, - {DomCode::SELECT, DomKey::SELECT}, - // {DomCode::SELECT_TASK, DomKey::_} - {DomCode::SHIFT_LEFT, DomKey::SHIFT}, - {DomCode::SHIFT_RIGHT, DomKey::SHIFT}, - {DomCode::SUPER, DomKey::SUPER}, - {DomCode::TAB, DomKey::TAB, 0x0009}, - {DomCode::UNDO, DomKey::UNDO}, - // {DomCode::VOICE_COMMAND, DomKey::_} - {DomCode::VOLUME_DOWN, DomKey::VOLUME_DOWN}, - {DomCode::VOLUME_MUTE, DomKey::VOLUME_MUTE}, - {DomCode::VOLUME_UP, DomKey::VOLUME_UP}, - {DomCode::WAKE_UP, DomKey::WAKE_UP}, - {DomCode::ZOOM_TOGGLE, DomKey::ZOOM_TOGGLE}, -}; - -} // anonymous namespace - StubKeyboardLayoutEngine::StubKeyboardLayoutEngine() { } @@ -259,42 +44,8 @@ base::char16* out_character, KeyboardCode* out_key_code, uint32* platform_keycode) const { - if ((flags & EF_CONTROL_DOWN) == EF_CONTROL_DOWN) { - if (LookupControlCharacter(dom_code, flags, out_dom_key, out_character, - out_key_code)) { - return true; - } - } else { - for (size_t i = 0; i < arraysize(printable_code_map); ++i) { - const PrintableCodeEntry* e = &printable_code_map[i]; - if (e->dom_code == dom_code) { - int state = (((flags & EF_ALTGR_DOWN) == EF_ALTGR_DOWN) << 1) | - ((flags & EF_SHIFT_DOWN) == EF_SHIFT_DOWN); - base::char16 ch = e->character[state]; - *out_dom_key = (ch & DK) ? DomKey::DEAD : DomKey::CHARACTER; - *out_character = ch; - if ((flags & EF_CAPS_LOCK_DOWN) == EF_CAPS_LOCK_DOWN) { - ch = (ch & ~DK) | 0x20; - if ((ch >= 'a') && (ch <= 'z')) - *out_character = e->character[state ^ 1]; - } - *out_key_code = DomCodeToNonLocatedKeyboardCode(dom_code); - return true; - } - } - } - - for (size_t i = 0; i < arraysize(non_printable_code_map); ++i) { - const NonPrintableCodeEntry* e = &non_printable_code_map[i]; - if (e->dom_code == dom_code) { - *out_dom_key = e->dom_key; - *out_character = e->character; - *out_key_code = NonPrintableDomKeyToKeyboardCode(e->dom_key); - return true; - } - } - - return false; + return DomCodeToUsLayoutMeaning(dom_code, flags, out_dom_key, out_character, + out_key_code); } } // namespace ui
diff --git a/ui/events/ozone/layout/xkb/xkb_keyboard_layout_engine.cc b/ui/events/ozone/layout/xkb/xkb_keyboard_layout_engine.cc index 63b89fe..2d47cafe2 100644 --- a/ui/events/ozone/layout/xkb/xkb_keyboard_layout_engine.cc +++ b/ui/events/ozone/layout/xkb/xkb_keyboard_layout_engine.cc
@@ -657,6 +657,17 @@ } #endif +KeyboardCode DomCodeToUsLayoutKeyboardCode(DomCode dom_code) { + DomKey dummy_dom_key; + base::char16 dummy_character; + KeyboardCode key_code; + if (DomCodeToUsLayoutMeaning(dom_code, EF_NONE, &dummy_dom_key, + &dummy_character, &key_code)) { + return key_code; + } + return VKEY_UNKNOWN; +} + } // anonymous namespace XkbKeyCodeConverter::XkbKeyCodeConverter() { @@ -775,21 +786,21 @@ *key_code = DifficultKeyboardCode(dom_code, flags, xkb_keycode, xkb_flags, xkb_keysym, *dom_key, *character); if (*key_code == VKEY_UNKNOWN) - *key_code = DomCodeToNonLocatedKeyboardCode(dom_code); + *key_code = DomCodeToUsLayoutKeyboardCode(dom_code); } if ((flags & EF_CONTROL_DOWN) == EF_CONTROL_DOWN) { - // Use GetCharacterFromKeyCode() to set |character| to 0x0 for key codes - // that we do not care about. + // Use GetCharacterFromKeyCode() to set |character| to 0x0 for keys that + // are not part of the accepted set of Control+Key combinations. *character = GetCharacterFromKeyCode(*key_code, flags); } } else if (*dom_key == DomKey::DEAD) { *character = DeadXkbKeySymToCombiningCharacter(xkb_keysym); - *key_code = DomCodeToNonLocatedKeyboardCode(dom_code); + *key_code = DomCodeToUsLayoutKeyboardCode(dom_code); } else { *key_code = NonPrintableDomKeyToKeyboardCode(*dom_key); if (*key_code == VKEY_UNKNOWN) - *key_code = DomCodeToNonLocatedKeyboardCode(dom_code); + *key_code = DomCodeToUsLayoutKeyboardCode(dom_code); } return true; }
diff --git a/ui/events/ozone/layout/xkb/xkb_keyboard_layout_engine.h b/ui/events/ozone/layout/xkb/xkb_keyboard_layout_engine.h index 382a9d3..099d7fd4 100644 --- a/ui/events/ozone/layout/xkb/xkb_keyboard_layout_engine.h +++ b/ui/events/ozone/layout/xkb/xkb_keyboard_layout_engine.h
@@ -58,7 +58,8 @@ // Determines the Windows-based KeyboardCode (VKEY) for a character key, // accounting for non-US layouts. May return VKEY_UNKNOWN, in which case the - // caller should use |DomCodeToNonLocatedKeyboardCode()| as a last resort. + // caller should, as a last resort, obtain a KeyboardCode using + // |DomCodeToUsLayoutMeaning()|. KeyboardCode DifficultKeyboardCode(DomCode dom_code, int ui_flags, xkb_keycode_t xkb_keycode,
diff --git a/ui/events/ozone/layout/xkb/xkb_keyboard_layout_engine_unittest.cc b/ui/events/ozone/layout/xkb/xkb_keyboard_layout_engine_unittest.cc index 87b4c55..f5195d0 100644 --- a/ui/events/ozone/layout/xkb/xkb_keyboard_layout_engine_unittest.cc +++ b/ui/events/ozone/layout/xkb/xkb_keyboard_layout_engine_unittest.cc
@@ -6,8 +6,8 @@ #include "ui/events/event_constants.h" #include "ui/events/keycodes/dom3/dom_code.h" #include "ui/events/keycodes/dom3/dom_key.h" +#include "ui/events/keycodes/keyboard_code_conversion.h" #include "ui/events/ozone/layout/keyboard_layout_engine_manager.h" -#include "ui/events/ozone/layout/layout_util.h" #include "ui/events/ozone/layout/xkb/xkb_keyboard_layout_engine.h" namespace ui { @@ -72,16 +72,21 @@ KeyboardCode key_code = DifficultKeyboardCode( dom_code, flags, key_code_converter_.DomCodeToXkbKeyCode(dom_code), flags, CharacterToKeySym(character), DomKey::CHARACTER, character); - if (key_code == VKEY_UNKNOWN) - key_code = DomCodeToNonLocatedKeyboardCode(dom_code); + if (key_code == VKEY_UNKNOWN) { + DomKey dummy_dom_key; + base::char16 dummy_character; + // If this fails, key_code remains VKEY_UNKNOWN. + ignore_result(DomCodeToUsLayoutMeaning(dom_code, EF_NONE, &dummy_dom_key, + &dummy_character, &key_code)); + } return key_code; } // XkbKeyboardLayoutEngine overrides: bool XkbLookup(xkb_keycode_t xkb_keycode, - xkb_mod_mask_t xkb_flags, - xkb_keysym_t* xkb_keysym, - base::char16* character) const override { + xkb_mod_mask_t xkb_flags, + xkb_keysym_t* xkb_keysym, + base::char16* character) const override { if (!entry_ || (xkb_keycode != static_cast<xkb_keycode_t>(entry_->dom_code))) return false; @@ -731,14 +736,14 @@ if (e->shift_character) { // Test with predetermined shifted character. key_code = layout_engine_->GetKeyboardCode(e->dom_code, EF_SHIFT_DOWN, - e->shift_character); + e->shift_character); EXPECT_EQ(e->key_code, key_code); } if (e->altgr_character) { // Test with predetermined AltGr character. key_code = layout_engine_->GetKeyboardCode(e->dom_code, EF_ALTGR_DOWN, - e->altgr_character); + e->altgr_character); EXPECT_EQ(e->key_code, key_code); }
diff --git a/ui/file_manager/file_manager/foreground/css/cws_widget_container.css b/ui/file_manager/file_manager/foreground/css/cws_widget_container.css index 8e4c5812..39f4a32 100644 --- a/ui/file_manager/file_manager/foreground/css/cws_widget_container.css +++ b/ui/file_manager/file_manager/foreground/css/cws_widget_container.css
@@ -12,26 +12,30 @@ position: relative; } -.cws-widget-webview-container.cws-widget-show-spinner webview { - pointer-events: none; -} - -.cws-widget-webview-container:not(.cws-widget-show-spinner) - .cws-widget-spinner-layer { - display: none; -} - .cws-widget-spinner-layer { background: url(../images/common/spinner.svg) center / 16px no-repeat; background-color: rgba(255, 255, 255, 0.7); bottom: 0; left: 0; + outline: none; position: absolute; right: 0; top: 0; + transition: opacity 500ms; z-index: 525; } +.cws-widget-spinner-layer:not(.cws-widget-show-spinner) { + background: none; + opacity: 0; + pointer-events: none; +} + +.cws-widget-spinner-layer.cws-widget-hiding-spinner { + /* Transition end event would not fire if opacity was set to 0. */ + opacity: 0.01; +} + .cws-widget-buttons { background: #eee; width: 100%;
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 b7e7b2c0..ff5f158b 100644 --- a/ui/file_manager/file_manager/foreground/css/file_manager.css +++ b/ui/file_manager/file_manager/foreground/css/file_manager.css
@@ -1162,7 +1162,7 @@ } body:not(.check-select) .thumbnail-grid.image-dominant - .thumbnail-loaded:not([selected]) .thumbnail-bottom { + .can-hide-filename.thumbnail-loaded:not([selected]) .thumbnail-bottom { display: none; }
diff --git a/ui/file_manager/file_manager/foreground/js/cws_widget_container.js b/ui/file_manager/file_manager/foreground/js/cws_widget_container.js index 45548c1..ad7eb42 100644 --- a/ui/file_manager/file_manager/foreground/js/cws_widget_container.js +++ b/ui/file_manager/file_manager/foreground/js/cws_widget_container.js
@@ -75,7 +75,14 @@ */ var spinnerLayer = document.createElement('div'); spinnerLayer.className = 'cws-widget-spinner-layer'; - this.webviewContainer_.appendChild(spinnerLayer); + spinnerLayer.setAttribute('role', 'img'); + // TODO(tbarzic): Set something meaningfull. + spinnerLayer.setAttribute('alt', ''); + parentNode.appendChild(spinnerLayer); + + /** @private {!CWSWidgetContainer.SpinnerLayerController} */ + this.spinnerLayerController_ = + new CWSWidgetContainer.SpinnerLayerController(spinnerLayer); /** * The widget container's button strip. @@ -376,7 +383,8 @@ }); this.webviewContainer_.appendChild(this.webview_); - this.webviewContainer_.classList.add('cws-widget-show-spinner'); + this.spinnerLayerController_.setElementToFocusOnHide(this.webview_); + this.spinnerLayerController_.setVisible(true); this.webviewClient_ = new CWSContainerClient( this.webview_, @@ -436,9 +444,9 @@ CWSWidgetContainer.Metrics.recordLoad( CWSWidgetContainer.Metrics.LOAD.SUCCEEDED); - this.webviewContainer_.classList.remove('cws-widget-show-spinner'); this.state_ = CWSWidgetContainer.State.INITIALIZED; + this.spinnerLayerController_.setVisible(false); this.webview_.focus(); }; @@ -450,7 +458,7 @@ CWSWidgetContainer.prototype.onWidgetLoadFailed_ = function(event) { CWSWidgetContainer.Metrics.recordLoad(CWSWidgetContainer.Metrics.LOAD.FAILED); - this.webviewContainer_.classList.remove('cws-widget-show-spinner'); + this.spinnerLayerController_.setVisible(false); this.state_ = CWSWidgetContainer.State.INITIALIZE_FAILED_CLOSING; this.reportDone_(); }; @@ -477,7 +485,7 @@ this.appInstaller_ = new AppInstaller(itemId); this.appInstaller_.install(this.onItemInstalled_.bind(this)); - this.webviewContainer_.classList.add('cws-widget-show-spinner'); + this.spinnerLayerController_.setVisible(true); this.state_ = CWSWidgetContainer.State.INSTALLING; }; @@ -488,7 +496,7 @@ * @private */ CWSWidgetContainer.prototype.onInstallDone_ = function(e) { - this.webviewContainer_.classList.remove('cws-widget-show-spinner'); + this.spinnerLayerController_.setVisible(false); this.state_ = CWSWidgetContainer.State.INSTALLED_CLOSING; this.reportDone_(); }; @@ -505,7 +513,7 @@ // If install succeeded, the spinner will be removed once // |this.webviewClient_| dispatched INSTALL_DONE event. if (!success) - this.webviewContainer_.classList.remove('cws-widget-show-spinner'); + this.spinnerLayerController_.setVisible(false); this.state_ = success ? CWSWidgetContainer.State.WAITING_FOR_CONFIRMATION : @@ -640,6 +648,8 @@ this.resolveStart_ = null; } + this.spinnerLayerController_.reset(); + if (this.webviewClient_) { this.webviewClient_.dispose(); this.webviewClient_ = null; @@ -658,6 +668,138 @@ }; /** + * Controls showing and hiding spinner layer. + * @param {!Element} spinnerLayer The spinner layer element. + * @constructor + */ +CWSWidgetContainer.SpinnerLayerController = function(spinnerLayer) { + /** @private {!Element} */ + this.spinnerLayer_ = spinnerLayer; + + /** @private {boolean} */ + this.visible_ = false; + + /** + * Set only if spinner is transitioning between visible and hidden states. + * Calling the function clears event handlers set for handling the transition, + * and updates spinner layer class list to its final state. + * @type {?function()} + * @private + */ + this.clearTransition_ = null; + + /** + * Reference to the timeout set to ensure {@code this.clearTransision_} gets + * called even if 'transitionend' event does not fire. + * @type {?number} + * @private + */ + this.clearTransitionTimeout_ = null; + + /** + * Element to be focused when the layer is hidden. + * @type {Element} + * @private + */ + this.focusOnHide_ = null; + + spinnerLayer.tabIndex = -1; + + // Prevent default Tab key handling in order to prevent the widget from + // taking the focus while the spinner layer is active. + // NOTE: This assumes that there are no elements allowed to become active + // while the spinner is shown. Something smarter would be needed if this + // assumption becomes invalid. + spinnerLayer.addEventListener('keydown', this.handleKeyDown_.bind(this)); +}; + +/** + * Sets element to be focused when the layer is hidden. + * @param {!Element} el + */ +CWSWidgetContainer.SpinnerLayerController.prototype.setElementToFocusOnHide = + function(el) { + this.focusOnHide_ = el; +}; + +/** + * Prevents default Tab key handling in order to prevent spinner layer from + * losing focus. + * @param {Event} e The key down event. + * @private + */ +CWSWidgetContainer.SpinnerLayerController.prototype.handleKeyDown_ = + function(e) { + if (!this.visible_) + return; + if (e.keyCode === 9 /* Tab */) + e.preventDefault(); +}; + +/** + * Resets the spinner layer controllers state, and makes sure the spinner + * layre gets hidden. + */ +CWSWidgetContainer.SpinnerLayerController.prototype.reset = function() { + this.visible_ = false; + this.focusOnHide_ = null; + if (this.clearTransision_) + this.clearTransition_(); +}; + +/** + * Shows or hides the spinner layer and handles the layer's opacity transition. + * @param {boolean} visible Whether the layer should become visible. + */ +CWSWidgetContainer.SpinnerLayerController.prototype.setVisible = + function(visible) { + if (this.visible_ === visible) + return; + + if (this.clearTransition_) + this.clearTransition_(); + + this.visible_ = visible; + + // Spinner should be shown during transition. + if (!this.spinnerLayer_.classList.contains('cws-widget-show-spinner')) + this.spinnerLayer_.classList.add('cws-widget-show-spinner'); + + if (this.visible_) { + this.spinnerLayer_.focus(); + } else if (this.focusOnHide_) { + this.focusOnHide_.focus(); + } + + if (!this.visible_) + this.spinnerLayer_.classList.add('cws-widget-hiding-spinner'); + + this.clearTransition_ = function() { + if (this.clearTransitionTimeout_) + clearTimeout(this.clearTransitionTimeout_); + this.clearTransitionTimeout_ = null; + + this.spinnerLayer_.removeEventListener( + 'transitionend', this.clearTransition_); + this.clearTransition_ = null; + + if (!this.visible_) { + this.spinnerLayer_.classList.remove('cws-widget-hiding-spinner'); + this.spinnerLayer_.classList.remove('cws-widget-show-spinner'); + } + }.bind(this); + + this.spinnerLayer_.addEventListener('transitionend', this.clearTransition_); + + // Ensure the transition state gets cleared, even if transitionend is not + // fired. + this.clearTransitionTimeout_ = setTimeout(function() { + this.clearTransitionTimeout_ = null; + this.clearTransition_(); + }.bind(this), 550 /* ms */); +}; + +/** * Utility methods and constants to record histograms. */ CWSWidgetContainer.Metrics = {};
diff --git a/ui/file_manager/file_manager/foreground/js/directory_model.js b/ui/file_manager/file_manager/foreground/js/directory_model.js index 5b34d88..4dd71e8d 100644 --- a/ui/file_manager/file_manager/foreground/js/directory_model.js +++ b/ui/file_manager/file_manager/foreground/js/directory_model.js
@@ -946,23 +946,17 @@ var previousDirEntry = this.currentDirContents_.getDirectoryEntry(); + this.clearAndScan_( + newDirectoryContents, + function(result) { + // Calls the callback of the method when successful. + if (result && opt_callback) + opt_callback(); - var promises = []; - - promises.push( - new Promise( - /** @this {DirectoryModel} */ - function(resolve) { - this.clearAndScan_( - newDirectoryContents, - function(result) { - // Calls the callback of the method when successful. - if (result && opt_callback) - opt_callback(); - - resolve(undefined); - }); - }.bind(this))); + // Notify that the current task of this.directoryChangeQueue_ + // is completed. + setTimeout(queueTaskCallback, 0); + }); // For tests that open the dialog to empty directories, everything // is loaded at this point. @@ -976,13 +970,11 @@ event.previousDirEntry = previousDirEntry; event.newDirEntry = dirEntry; event.volumeChanged = previousVolumeInfo !== currentVolumeInfo; + this.dispatchEvent(event); if (event.volumeChanged) { - promises.push(this.onVolumeChanged_(currentVolumeInfo)); + this.onVolumeChanged_(assert(currentVolumeInfo)); } - - this.dispatchEvent(event); - Promise.all(promises).then(queueTaskCallback); }.bind(this)); }.bind(this, this.changeDirectorySequence_)); };
diff --git a/ui/file_manager/file_manager/foreground/js/file_manager.js b/ui/file_manager/file_manager/foreground/js/file_manager.js index 5fa935ff..14fc3b7 100644 --- a/ui/file_manager/file_manager/foreground/js/file_manager.js +++ b/ui/file_manager/file_manager/foreground/js/file_manager.js
@@ -247,7 +247,7 @@ /** * The document object of this app. - * @type {HTMLDocument} + * @type {Document} * @private */ this.document_ = null; @@ -297,7 +297,7 @@ return this.ui_.directoryTree; }, /** - * @return {HTMLDocument} + * @return {Document} */ get document() { return this.document_; @@ -450,9 +450,9 @@ this.importController_ = new importer.ImportController( new importer.RuntimeControllerEnvironment( this, - this.selectionHandler_), - this.mediaScanner_, - this.mediaImportHandler_, + assert(this.selectionHandler_)), + assert(this.mediaScanner_), + assert(this.mediaImportHandler_), new importer.RuntimeCommandWidget(), assert(this.tracker_)); }
diff --git a/ui/file_manager/file_manager/foreground/js/file_transfer_controller.js b/ui/file_manager/file_manager/foreground/js/file_transfer_controller.js index 2eeb6a7..ce4b01a 100644 --- a/ui/file_manager/file_manager/foreground/js/file_transfer_controller.js +++ b/ui/file_manager/file_manager/foreground/js/file_transfer_controller.js
@@ -16,7 +16,7 @@ var FileAsyncData; /** - * @param {!HTMLDocument} doc Owning document. + * @param {!Document} doc Owning document. * @param {!DirectoryTree} directoryTree Directory tree. * @param {!ListContainer} listContainer List container. * @param {!MultiProfileShareDialog} multiProfileShareDialog Share dialog to be @@ -44,7 +44,7 @@ volumeManager, selectionHandler) { /** - * @type {!HTMLDocument} + * @type {!Document} * @private * @const */
diff --git a/ui/file_manager/file_manager/foreground/js/thumbnail_loader.js b/ui/file_manager/file_manager/foreground/js/thumbnail_loader.js index 89ab4b5..7049bae3 100644 --- a/ui/file_manager/file_manager/foreground/js/thumbnail_loader.js +++ b/ui/file_manager/file_manager/foreground/js/thumbnail_loader.js
@@ -188,7 +188,7 @@ this.canvasUpToDate_ = false; this.image_ = new Image(); this.image_.onload = function() { - this.attachImage(box, fillMode); + this.attachImage(assert(box), fillMode); if (opt_onSuccess) opt_onSuccess(this.image_, this.transform_); }.bind(this);
diff --git a/ui/file_manager/file_manager/foreground/js/ui/file_grid.js b/ui/file_manager/file_manager/foreground/js/ui/file_grid.js index cfb1fa4f..bf208d0 100644 --- a/ui/file_manager/file_manager/foreground/js/ui/file_grid.js +++ b/ui/file_manager/file_manager/foreground/js/ui/file_grid.js
@@ -36,8 +36,10 @@ if (this.dataModel) this.dataModel.removeEventListener('splice', this.onSplice_.bind(this)); this.dataModelDescriptor_.set.call(this, model); - if (this.dataModel) + if (this.dataModel) { this.dataModel.addEventListener('splice', this.onSplice_.bind(this)); + this.classList.toggle('image-dominant', this.dataModel.isImageDominant()); + } } }; @@ -591,6 +593,7 @@ box.setAttribute('generic-thumbnail', mediaType); li.classList.toggle('thumbnail-loaded', false); } + li.classList.toggle('can-hide-filename', FileType.isImage(entry)); }; /** @@ -890,6 +893,8 @@ return -1; var row = this.grid_.getItemRow(index); + if (row - 1 < 0) + return 0; var col = this.grid_.getItemColumn(index); var nextIndex = this.grid_.getItemIndex(row - 1, col); if (nextIndex === -1) {
diff --git a/ui/file_manager/gallery/css/gallery.css b/ui/file_manager/gallery/css/gallery.css index 3caae070..7b933b20 100644 --- a/ui/file_manager/gallery/css/gallery.css +++ b/ui/file_manager/gallery/css/gallery.css
@@ -104,7 +104,7 @@ pointer-events: none; position: absolute; /* Duration and timing function are set in Javascript. */ - transition-property: -webkit-transform, opacity; + transition-property: transform, opacity; } .gallery .image-container > .image[fade] { @@ -113,7 +113,8 @@ /* Full resolution image is invisible unless printing. */ .gallery .image-container > canvas.fullres { - display: none; + opacity: 0; + position: absolute; } @media print { @@ -138,8 +139,13 @@ /* Print the full resolution image instead. */ .gallery .image-container > canvas.fullres { display: block !important; + height: auto !important; max-height: 100%; max-width: 100%; + position: static !important; + transform: none !important; + visibility: visible !important; + width: auto !important; } } @@ -392,7 +398,7 @@ } .gallery .filename-spacer .saved[highlighted] { - -webkit-transform: scaleX(1.1) scaleY(1.1) rotate(0); + transform: scaleX(1.1) scaleY(1.1) rotate(0); opacity: 1; } @@ -1152,7 +1158,7 @@ top: 0; /* transition-duration is set in Javascript. */ - transition-property: -webkit-transform; + transition-property: transform; transition-timing-function: linear; } @@ -1172,7 +1178,7 @@ position: absolute; /* Tile's zoom factor is animated on hover. We apply the transform to the entire tile so that the image outline is included into the animation. */ - transition: -webkit-transform 150ms linear; + transition: transform 150ms linear; z-index: 50; }
diff --git a/ui/file_manager/gallery/js/image_editor/image_view.js b/ui/file_manager/gallery/js/image_editor/image_view.js index 76b837f..0737ce8 100644 --- a/ui/file_manager/gallery/js/image_editor/image_view.js +++ b/ui/file_manager/gallery/js/image_editor/image_view.js
@@ -179,6 +179,11 @@ ImageView.prototype.draw = function() { if (!this.contentCanvas_) // Do nothing if the image content is not set. return; + this.setTransform_( + this.contentCanvas_, + this.viewport_, + new ImageView.Effect.None(), + ImageView.Effect.DEFAULT_DURATION); if ((this.screenImage_ && this.setupDeviceBuffer(this.screenImage_)) || this.displayedContentGeneration_ !== this.contentGeneration_) { this.displayedContentGeneration_ = this.contentGeneration_; @@ -194,13 +199,23 @@ * change or offset change) with animation. */ ImageView.prototype.applyViewportChange = function() { - if (this.screenImage_) { + var zooming = this.viewport_.getZoom() > 1; + if (this.contentCanvas_) { + // Show full resolution image only for zooming. + this.contentCanvas_.style.opacity = zooming ? '1' : '0'; this.setTransform_( - this.screenImage_, + this.contentCanvas_, this.viewport_, new ImageView.Effect.None(), ImageView.Effect.DEFAULT_DURATION); } + if (this.screenImage_) { + this.setTransform_( + this.screenImage_, + this.viewport_, + new ImageView.Effect.None(), + ImageView.Effect.DEFAULT_DURATION); + } }; /** @@ -300,16 +315,7 @@ canvas.height = deviceRect.height; needRepaint = true; } - - // Center the image. - var imageBounds = this.viewport_.getImageElementBoundsOnScreen(); - canvas.style.left = imageBounds.left + 'px'; - canvas.style.top = imageBounds.top + 'px'; - canvas.style.width = imageBounds.width + 'px'; - canvas.style.height = imageBounds.height + 'px'; - this.setTransform_(canvas, this.viewport_); - return needRepaint; }; @@ -562,6 +568,8 @@ // Insert the full resolution canvas into DOM so that it can be printed. this.container_.appendChild(this.contentCanvas_); this.contentCanvas_.classList.add('fullres'); + this.setTransform_( + this.contentCanvas_, this.viewport_, null, 0); this.contentItem_.contentImage = this.contentCanvas_; this.contentItem_.screenImage = this.screenImage_; @@ -622,7 +630,6 @@ content, opt_effect, opt_width, opt_height, opt_preview) { var oldScreenImage = this.screenImage_; var oldViewport = this.viewport_.clone(); - this.replaceContent_(content, opt_width, opt_height, opt_preview); if (!opt_effect) { if (oldScreenImage) @@ -638,33 +645,47 @@ ImageUtil.setAttribute(newScreenImage, 'fade', true); this.setTransform_( newScreenImage, this.viewport_, opt_effect, 0 /* instant */); + this.setTransform_( + content, this.viewport_, opt_effect, 0 /* instant */); - setTimeout(function() { - this.setTransform_( - newScreenImage, - this.viewport_, - null, - opt_effect ? opt_effect.getDuration() : undefined); - if (oldScreenImage) { - ImageUtil.setAttribute(newScreenImage, 'fade', false); - ImageUtil.setAttribute(oldScreenImage, 'fade', true); - var reverse = opt_effect.getReverse(); - if (reverse) { - this.setTransform_(oldScreenImage, oldViewport, reverse); - setTimeout(function() { - if (oldScreenImage.parentNode) - oldScreenImage.parentNode.removeChild(oldScreenImage); - }, reverse.getSafeInterval()); - } else { - if (oldScreenImage.parentNode) - oldScreenImage.parentNode.removeChild(oldScreenImage); + // We need to call requestAnimationFrame twice here. The first call is for + // commiting the styles of beggining of transition that are assigned above. + // The second call is for assigning and commiting the styles of end of + // transition, which triggers transition animation. + requestAnimationFrame(function() { + requestAnimationFrame(function() { + this.setTransform_( + newScreenImage, + this.viewport_, + null, + opt_effect ? opt_effect.getDuration() : undefined); + this.setTransform_( + content, + this.viewport_, + null, + opt_effect ? opt_effect.getDuration() : undefined); + if (oldScreenImage) { + ImageUtil.setAttribute(newScreenImage, 'fade', false); + ImageUtil.setAttribute(oldScreenImage, 'fade', true); + var reverse = opt_effect.getReverse(); + if (reverse) { + this.setTransform_(oldScreenImage, oldViewport, reverse); + setTimeout(function() { + if (oldScreenImage.parentNode) + oldScreenImage.parentNode.removeChild(oldScreenImage); + }, reverse.getSafeInterval()); + } else { + if (oldScreenImage.parentNode) + oldScreenImage.parentNode.removeChild(oldScreenImage); + } } - } - }.bind(this), 0); + }.bind(this)); + }.bind(this)); }; /** - * @param {!HTMLCanvasElement} element The element to transform. + * @param {!HTMLCanvasElement|!HTMLImageElement} element The element to + * transform. * @param {!Viewport} viewport Viewport to be used for calculating * transformation. * @param {ImageView.Effect=} opt_effect The effect to apply. @@ -677,9 +698,14 @@ opt_effect = new ImageView.Effect.None(); if (typeof opt_duration !== 'number') opt_duration = opt_effect.getDuration(); - element.style.webkitTransitionDuration = opt_duration + 'ms'; - element.style.webkitTransitionTimingFunction = opt_effect.getTiming(); - element.style.webkitTransform = opt_effect.transform(element, viewport); + element.style.transitionDuration = opt_duration + 'ms'; + element.style.transitionTimingFunction = opt_effect.getTiming(); + element.style.transform = opt_effect.transform(element, viewport); + var imageBounds = viewport.getImageElementBoundsOnScreen(); + element.style.left = imageBounds.left + 'px'; + element.style.top = imageBounds.top + 'px'; + element.style.width = imageBounds.width + 'px'; + element.style.height = imageBounds.height + 'px'; }; /** @@ -823,8 +849,8 @@ /** * Obtains the CSS transformation string of the effect. - * @param {!HTMLCanvasElement} element Canvas element to be applied the - * transformation. + * @param {!HTMLCanvasElement|!HTMLImageElement} element Canvas element to be + * applied the transformation. * @param {!Viewport} viewport Current viewport. * @return {string} CSS transformation description. */ @@ -849,9 +875,7 @@ ImageView.Effect.None.prototype = { __proto__: ImageView.Effect.prototype }; /** - * @param {!HTMLCanvasElement} element Element. - * @param {!Viewport} viewport Current viewport. - * @return {string} Transform string. + * @override */ ImageView.Effect.None.prototype.transform = function(element, viewport) { return viewport.getTransformation();
diff --git a/ui/file_manager/gallery/js/mosaic_mode.js b/ui/file_manager/gallery/js/mosaic_mode.js index ea5e355f1..1138a674 100644 --- a/ui/file_manager/gallery/js/mosaic_mode.js +++ b/ui/file_manager/gallery/js/mosaic_mode.js
@@ -711,9 +711,9 @@ */ Mosaic.prototype.transform = function(tileRect, imageRect, opt_instant) { if (opt_instant) { - this.style.webkitTransitionDuration = '0'; + this.style.transitionDuration = '0'; } else { - this.style.webkitTransitionDuration = + this.style.transitionDuration = ImageView.MODE_TRANSITION_DURATION + 'ms'; } @@ -724,11 +724,11 @@ (tileRect.left + tileRect.width / 2); var shiftY = (imageRect.top + imageRect.height / 2) - (tileRect.top + tileRect.height / 2); - this.style.webkitTransform = + this.style.transform = 'translate(' + shiftX * scaleX + 'px, ' + shiftY * scaleY + 'px)' + 'scaleX(' + scaleX + ') scaleY(' + scaleY + ')'; } else { - this.style.webkitTransform = ''; + this.style.transform = ''; } };
diff --git a/ui/gfx/display.cc b/ui/gfx/display.cc index 63353713..6ae51b6 100644 --- a/ui/gfx/display.cc +++ b/ui/gfx/display.cc
@@ -198,12 +198,19 @@ return is_valid() && (id_ == internal_display_id_); } +// static int64 Display::InternalDisplayId() { return internal_display_id_; } +// static void Display::SetInternalDisplayId(int64 internal_display_id) { internal_display_id_ = internal_display_id; } +// static +bool Display::HasInternalDisplay() { + return internal_display_id_ != kInvalidDisplayID; +} + } // namespace gfx
diff --git a/ui/gfx/display.h b/ui/gfx/display.h index fe2a40c..6f29d42 100644 --- a/ui/gfx/display.h +++ b/ui/gfx/display.h
@@ -122,6 +122,9 @@ static int64 InternalDisplayId(); static void SetInternalDisplayId(int64 internal_display_id); + // True if there is an internal display. + static bool HasInternalDisplay(); + static const int64 kInvalidDisplayID; private:
diff --git a/ui/gfx/ios/OWNERS b/ui/gfx/ios/OWNERS index f2fff55..ae82009 100644 --- a/ui/gfx/ios/OWNERS +++ b/ui/gfx/ios/OWNERS
@@ -1,3 +1,2 @@ -lliabraa@chromium.org rohitrao@chromium.org sdefresne@chromium.org
diff --git a/ui/gfx/render_text_unittest.cc b/ui/gfx/render_text_unittest.cc index fe4899ea..1ceec517 100644 --- a/ui/gfx/render_text_unittest.cc +++ b/ui/gfx/render_text_unittest.cc
@@ -587,13 +587,20 @@ { L"01" L"\x5d0\x5d1\x5d2", L"01\x5d0\x5d1\x2026", true }, // RLM marker added as "ab" have strong LTR directionality. { L"ab" L"\x5d0\x5d1\x5d2", L"ab\x5d0\x5d1\x2026\x200f", true }, - // Complex script is not handled. In this example, the "\x0915\x093f" is a - // compound glyph, but only half of it is elided. - { L"0123\x0915\x093f", L"0123\x0915\x2026", true }, - // Surrogate pairs should be elided reasonably enough. + // Test surrogate pairs. \xd834\xdd1e forms a single code point U+1D11E; + // \xd834\xdd22 forms a second code point U+1D122. The first should be kept; + // the second removed (no surrogate pair should be partially elided). + { L"0123\xd834\xdd1e\xd834\xdd22", L"0123\xd834\xdd1e\x2026", true }, + // Test combining character sequences. U+0915 U+093F forms a compound glyph; + // U+0915 U+0942 forms a second compound glyph. The first should be kept; + // the second removed (no combining sequence should be partially elided). + { L"0123\x0915\x093f\x0915\x0942", L"0123\x0915\x093f\x2026", true }, + // U+05E9 U+05BC U+05C1 U+05B8 forms a four-character compound glyph. Again, + // it should be either fully elided, or not elided at all. If completely + // elided, an LTR Mark (U+200E) should be added. { L"0\x05e9\x05bc\x05c1\x05b8", L"0\x05e9\x05bc\x05c1\x05b8", false }, - { L"0\x05e9\x05bc\x05c1\x05b8", L"0\x05e9\x05bc\x2026" , true }, - { L"01\x05e9\x05bc\x05c1\x05b8", L"01\x05e9\x2026" , true }, + { L"0\x05e9\x05bc\x05c1\x05b8", L"0\x2026\x200E" , true }, + { L"01\x05e9\x05bc\x05c1\x05b8", L"01\x2026\x200E" , true }, { L"012\x05e9\x05bc\x05c1\x05b8", L"012\x2026\x200E" , true }, { L"012\xF0\x9D\x84\x9E", L"012\xF0\x2026" , true }, };
diff --git a/ui/gfx/text_elider.cc b/ui/gfx/text_elider.cc index afcdb1d..fcfc71f6 100644 --- a/ui/gfx/text_elider.cc +++ b/ui/gfx/text_elider.cc
@@ -9,6 +9,8 @@ #include "ui/gfx/text_elider.h" +#include <stdint.h> + #include <string> #include <vector> @@ -23,7 +25,10 @@ #include "base/strings/sys_string_conversions.h" #include "base/strings/utf_string_conversions.h" #include "third_party/icu/source/common/unicode/rbbi.h" +#include "third_party/icu/source/common/unicode/uchar.h" #include "third_party/icu/source/common/unicode/uloc.h" +#include "third_party/icu/source/common/unicode/umachine.h" +#include "third_party/icu/source/common/unicode/utf16.h" #include "ui/gfx/font_list.h" #include "ui/gfx/geometry/rect_conversions.h" #include "ui/gfx/render_text.h" @@ -99,6 +104,22 @@ } #endif +// Returns true if the code point |c| is a combining mark character in Unicode. +bool CharIsMark(UChar32 c) { + int8_t char_type = u_charType(c); + return char_type == U_NON_SPACING_MARK || char_type == U_ENCLOSING_MARK || + char_type == U_COMBINING_SPACING_MARK; +} + +// Gets the code point of |str| at the given code unit position |index|. If +// |index| is a surrogate code unit, returns the whole code point (unless the +// code unit is unpaired, in which case it just returns the surrogate value). +UChar32 GetCodePointAt(const base::string16& str, size_t index) { + UChar32 c; + U16_GET(str.data(), 0, index, str.size(), c); + return c; +} + } // namespace // U+2026 in utf8 @@ -116,7 +137,8 @@ elide_at_beginning_(elide_at_beginning) { } -base::string16 StringSlicer::CutString(size_t length, bool insert_ellipsis) { +base::string16 StringSlicer::CutString(size_t length, + bool insert_ellipsis) const { const base::string16 ellipsis_text = insert_ellipsis ? ellipsis_ : base::string16(); @@ -130,18 +152,25 @@ // We put the extra character, if any, before the cut. const size_t half_length = length / 2; const size_t prefix_length = FindValidBoundaryBefore(length - half_length); - const size_t suffix_start_guess = text_.length() - half_length; - const size_t suffix_start = FindValidBoundaryAfter(suffix_start_guess); - const size_t suffix_length = - half_length - (suffix_start_guess - suffix_start); + const size_t suffix_start = + FindValidBoundaryAfter(text_.length() - half_length); return text_.substr(0, prefix_length) + ellipsis_text + - text_.substr(suffix_start, suffix_length); + text_.substr(suffix_start); } size_t StringSlicer::FindValidBoundaryBefore(size_t index) const { - DCHECK_LE(index, text_.length()); - if (index != text_.length()) - U16_SET_CP_START(text_.data(), 0, index); + size_t length = text_.length(); + DCHECK_LE(index, length); + if (index == length) + return index; + + // If |index| straddles a combining character sequence, go back until we find + // a base character. + while (index > 0 && CharIsMark(GetCodePointAt(text_, index))) + --index; + + // If |index| straddles a UTF-16 surrogate pair, go back. + U16_SET_CP_START(text_.data(), 0, index); return index; } @@ -152,6 +181,15 @@ int32_t text_index = base::checked_cast<int32_t>(index); int32_t text_length = base::checked_cast<int32_t>(text_.length()); + + // If |index| straddles a combining character sequence, go forward until we + // find a base character. + while (text_index < text_length && + CharIsMark(GetCodePointAt(text_, text_index))) { + ++text_index; + } + + // If |index| straddles a UTF-16 surrogate pair, go forward. U16_SET_CP_LIMIT(text_.data(), 0, text_index, text_length); return static_cast<size_t>(text_index); } @@ -245,12 +283,13 @@ size_t lo = 0; size_t hi = text.length() - 1; size_t guess; + base::string16 cut; for (guess = (lo + hi) / 2; lo <= hi; guess = (lo + hi) / 2) { // We check the width of the whole desired string at once to ensure we // handle kerning/ligatures/etc. correctly. // TODO(skanuj) : Handle directionality of ellipsis based on adjacent // characters. See crbug.com/327963. - const base::string16 cut = slicer.CutString(guess, insert_ellipsis); + cut = slicer.CutString(guess, insert_ellipsis); const float guess_width = GetStringWidthF(cut, font_list); if (guess_width == available_pixel_width) break; @@ -264,7 +303,7 @@ } } - return slicer.CutString(guess, insert_ellipsis); + return cut; #endif }
diff --git a/ui/gfx/text_elider.h b/ui/gfx/text_elider.h index 6b24ea6..1269caf 100644 --- a/ui/gfx/text_elider.h +++ b/ui/gfx/text_elider.h
@@ -28,20 +28,27 @@ GFX_EXPORT extern const base::char16 kEllipsisUTF16[]; GFX_EXPORT extern const base::char16 kForwardSlash; -// Helper class to split + elide text, while respecting UTF16 surrogate pairs. -class StringSlicer { +// Helper class to split + elide text, while respecting UTF-16 surrogate pairs +// and combining character sequences. +class GFX_EXPORT StringSlicer { public: + // Warning: Retains a reference to |text| and |ellipsis|. They must have a + // longer lifetime than the StringSlicer. StringSlicer(const base::string16& text, const base::string16& ellipsis, bool elide_in_middle, bool elide_at_beginning); - // Cuts |text_| to be |length| characters long. If |elide_in_middle_| is true, - // the middle of the string is removed to leave equal-length pieces from the - // beginning and end of the string; otherwise, the end of the string is - // removed and only the beginning remains. If |insert_ellipsis| is true, - // then an ellipsis character will be inserted at the cut point. - base::string16 CutString(size_t length, bool insert_ellipsis); + // Cuts |text_| to be at most |length| UTF-16 code units long. If + // |elide_in_middle_| is true, the middle of the string is removed to leave + // equal-length pieces from the beginning and end of the string; otherwise, + // the end of the string is removed and only the beginning remains. If + // |insert_ellipsis| is true, then an ellipsis character will be inserted at + // the cut point (note that the ellipsis will does not count towards the + // |length| limit). + // Note: Characters may still be omitted even if |length| is the full string + // length, if surrogate pairs fall on the split boundary. + base::string16 CutString(size_t length, bool insert_ellipsis) const; private: // Returns a valid cut boundary at or before/after |index|.
diff --git a/ui/gfx/text_elider_unittest.cc b/ui/gfx/text_elider_unittest.cc index 01269ae..641a31f 100644 --- a/ui/gfx/text_elider_unittest.cc +++ b/ui/gfx/text_elider_unittest.cc
@@ -6,6 +6,8 @@ #include "ui/gfx/text_elider.h" +#include <vector> + #include "base/files/file_path.h" #include "base/i18n/rtl.h" #include "base/memory/scoped_ptr.h" @@ -299,10 +301,11 @@ } // Checks that all occurrences of |first_char| are followed by |second_char| and -// all occurrences of |second_char| are preceded by |first_char| in |text|. -static void CheckSurrogatePairs(const base::string16& text, - base::char16 first_char, - base::char16 second_char) { +// all occurrences of |second_char| are preceded by |first_char| in |text|. Can +// be used to test surrogate pairs or two-character combining sequences. +static void CheckCodeUnitPairs(const base::string16& text, + base::char16 first_char, + base::char16 second_char) { for (size_t index = 0; index < text.length(); ++index) { EXPECT_NE(second_char, text[index]); if (text[index] == first_char) { @@ -312,37 +315,49 @@ } } +// Test that both both UTF-16 surrogate pairs and combining character sequences +// do not get split by ElideText. // TODO(338784): Enable this on android. #if defined(OS_ANDROID) -#define MAYBE_ElideTextSurrogatePairs DISABLED_ElideTextSurrogatePairs +#define MAYBE_ElideTextAtomicSequences DISABLED_ElideTextAtomicSequences #else -#define MAYBE_ElideTextSurrogatePairs ElideTextSurrogatePairs +#define MAYBE_ElideTextAtomicSequences ElideTextAtomicSequences #endif -TEST(TextEliderTest, MAYBE_ElideTextSurrogatePairs) { +TEST(TextEliderTest, MAYBE_ElideTextAtomicSequences) { const FontList font_list; - // The below is 'MUSICAL SYMBOL G CLEF', which is represented in UTF-16 as - // two characters forming a surrogate pair 0x0001D11E. - const std::string kSurrogate = "\xF0\x9D\x84\x9E"; - const base::string16 kTestString = UTF8ToUTF16(kSurrogate + "x" + kSurrogate); - const float kTestStringWidth = GetStringWidthF(kTestString, font_list); - const base::char16 kSurrogateFirstChar = kTestString[0]; - const base::char16 kSurrogateSecondChar = kTestString[1]; - base::string16 result; + // The below is 'MUSICAL SYMBOL G CLEF' (U+1D11E), which is represented in + // UTF-16 as two code units forming a surrogate pair: 0xD834 0xDD1E. + const base::char16 kSurrogate[] = {0xD834, 0xDD1E, 0}; + // The below is a Devanagari two-character combining sequence U+0921 U+093F. + // The sequence forms a single display character and should not be separated. + const base::char16 kCombiningSequence[] = {0x921, 0x93F, 0}; + std::vector<base::string16> pairs; + pairs.push_back(kSurrogate); + pairs.push_back(kCombiningSequence); - // Elide |kTextString| to all possible widths and check that no instance of - // |kSurrogate| was split in two. - for (float width = 0; width <= kTestStringWidth; width++) { - result = ElideText(kTestString, font_list, width, TRUNCATE); - CheckSurrogatePairs(result, kSurrogateFirstChar, kSurrogateSecondChar); + for (const base::string16& pair : pairs) { + base::char16 first_char = pair[0]; + base::char16 second_char = pair[1]; + base::string16 test_string = pair + UTF8ToUTF16("x") + pair; + SCOPED_TRACE(test_string); + const float test_string_width = GetStringWidthF(test_string, font_list); + base::string16 result; - result = ElideText(kTestString, font_list, width, ELIDE_TAIL); - CheckSurrogatePairs(result, kSurrogateFirstChar, kSurrogateSecondChar); + // Elide |text_string| to all possible widths and check that no instance of + // |pair| was split in two. + for (float width = 0; width <= test_string_width; width++) { + result = ElideText(test_string, font_list, width, TRUNCATE); + CheckCodeUnitPairs(result, first_char, second_char); - result = ElideText(kTestString, font_list, width, ELIDE_MIDDLE); - CheckSurrogatePairs(result, kSurrogateFirstChar, kSurrogateSecondChar); + result = ElideText(test_string, font_list, width, ELIDE_TAIL); + CheckCodeUnitPairs(result, first_char, second_char); - result = ElideText(kTestString, font_list, width, ELIDE_HEAD); - CheckSurrogatePairs(result, kSurrogateFirstChar, kSurrogateSecondChar); + result = ElideText(test_string, font_list, width, ELIDE_MIDDLE); + CheckCodeUnitPairs(result, first_char, second_char); + + result = ElideText(test_string, font_list, width, ELIDE_HEAD); + CheckCodeUnitPairs(result, first_char, second_char); + } } } @@ -440,6 +455,136 @@ } } +// Detailed tests for StringSlicer. These are faster and test more of the edge +// cases than the above tests which are more end-to-end. + +TEST(TextEliderTest, StringSlicerBasicTest) { + // Must store strings in variables (StringSlicer retains a reference to them). + base::string16 text(UTF8ToUTF16("Hello, world!")); + base::string16 ellipsis(kEllipsisUTF16); + StringSlicer slicer(text, ellipsis, false, false); + + EXPECT_EQ(UTF8ToUTF16(""), slicer.CutString(0, false)); + EXPECT_EQ(base::string16(kEllipsisUTF16), slicer.CutString(0, true)); + + EXPECT_EQ(UTF8ToUTF16("Hell"), slicer.CutString(4, false)); + EXPECT_EQ(UTF8ToUTF16("Hell") + kEllipsisUTF16, slicer.CutString(4, true)); + + EXPECT_EQ(text, slicer.CutString(text.length(), false)); + EXPECT_EQ(text + kEllipsisUTF16, slicer.CutString(text.length(), true)); + + StringSlicer slicer_begin(text, ellipsis, false, true); + EXPECT_EQ(UTF8ToUTF16("rld!"), slicer_begin.CutString(4, false)); + EXPECT_EQ(kEllipsisUTF16 + UTF8ToUTF16("rld!"), + slicer_begin.CutString(4, true)); + + StringSlicer slicer_mid(text, ellipsis, true, false); + EXPECT_EQ(UTF8ToUTF16("Held!"), slicer_mid.CutString(5, false)); + EXPECT_EQ(UTF8ToUTF16("Hel") + kEllipsisUTF16 + UTF8ToUTF16("d!"), + slicer_mid.CutString(5, true)); +} + +TEST(TextEliderTest, StringSlicerSurrogate) { + // The below is 'MUSICAL SYMBOL G CLEF' (U+1D11E), which is represented in + // UTF-16 as two code units forming a surrogate pair: 0xD834 0xDD1E. + const base::char16 kSurrogate[] = {0xD834, 0xDD1E, 0}; + base::string16 text(UTF8ToUTF16("abc") + kSurrogate + UTF8ToUTF16("xyz")); + base::string16 ellipsis(kEllipsisUTF16); + StringSlicer slicer(text, ellipsis, false, false); + + // Cut surrogate on the right. Should round left and exclude the surrogate. + EXPECT_EQ(base::string16(kEllipsisUTF16), slicer.CutString(0, true)); + EXPECT_EQ(UTF8ToUTF16("abc") + kEllipsisUTF16, slicer.CutString(4, true)); + EXPECT_EQ(text + kEllipsisUTF16, slicer.CutString(text.length(), true)); + + // Cut surrogate on the left. Should round left and include the surrogate. + StringSlicer slicer_begin(text, ellipsis, false, true); + EXPECT_EQ(base::string16(kEllipsisUTF16) + kSurrogate + UTF8ToUTF16("xyz"), + slicer_begin.CutString(4, true)); + + // Cut surrogate in the middle. Should round right and exclude the surrogate. + base::string16 short_text(UTF8ToUTF16("abc") + kSurrogate); + StringSlicer slicer_mid(short_text, ellipsis, true, false); + EXPECT_EQ(UTF8ToUTF16("a") + kEllipsisUTF16, slicer_mid.CutString(2, true)); + + // String that starts with a dangling trailing surrogate. + base::char16 dangling_trailing_chars[] = {kSurrogate[1], 0}; + base::string16 dangling_trailing_text(dangling_trailing_chars); + StringSlicer slicer_dangling_trailing(dangling_trailing_text, ellipsis, false, + false); + EXPECT_EQ(base::string16(kEllipsisUTF16), + slicer_dangling_trailing.CutString(0, true)); + EXPECT_EQ(dangling_trailing_text + kEllipsisUTF16, + slicer_dangling_trailing.CutString(1, true)); +} + +TEST(TextEliderTest, StringSlicerCombining) { + // The following string contains three combining character sequences (one for + // each category of combining mark): + // LATIN SMALL LETTER E + COMBINING ACUTE ACCENT + COMBINING CEDILLA + // LATIN SMALL LETTER X + COMBINING ENCLOSING KEYCAP + // DEVANAGARI LETTER DDA + DEVANAGARI VOWEL SIGN I + const base::char16 kText[] = { + 'e', 0x301, 0x327, ' ', 'x', 0x20E3, ' ', 0x921, 0x93F, 0}; + base::string16 text(kText); + base::string16 ellipsis(kEllipsisUTF16); + StringSlicer slicer(text, ellipsis, false, false); + + // Attempt to cut the string for all lengths. When a combining sequence is + // cut, it should always round left and exclude the combining sequence. + // First sequence: + EXPECT_EQ(base::string16(kEllipsisUTF16), slicer.CutString(0, true)); + EXPECT_EQ(base::string16(kEllipsisUTF16), slicer.CutString(1, true)); + EXPECT_EQ(base::string16(kEllipsisUTF16), slicer.CutString(2, true)); + EXPECT_EQ(text.substr(0, 3) + kEllipsisUTF16, slicer.CutString(3, true)); + // Second sequence: + EXPECT_EQ(text.substr(0, 4) + kEllipsisUTF16, slicer.CutString(4, true)); + EXPECT_EQ(text.substr(0, 4) + kEllipsisUTF16, slicer.CutString(5, true)); + EXPECT_EQ(text.substr(0, 6) + kEllipsisUTF16, slicer.CutString(6, true)); + // Third sequence: + EXPECT_EQ(text.substr(0, 7) + kEllipsisUTF16, slicer.CutString(7, true)); + EXPECT_EQ(text.substr(0, 7) + kEllipsisUTF16, slicer.CutString(8, true)); + EXPECT_EQ(text + kEllipsisUTF16, slicer.CutString(9, true)); + + // Cut string in the middle, splitting the second sequence in half. Should + // round both left and right, excluding the second sequence. + StringSlicer slicer_mid(text, ellipsis, true, false); + EXPECT_EQ(text.substr(0, 4) + kEllipsisUTF16 + text.substr(6), + slicer_mid.CutString(9, true)); + + // String that starts with a dangling combining mark. + base::char16 dangling_mark_chars[] = {text[1], 0}; + base::string16 dangling_mark_text(dangling_mark_chars); + StringSlicer slicer_dangling_mark(dangling_mark_text, ellipsis, false, false); + EXPECT_EQ(base::string16(kEllipsisUTF16), + slicer_dangling_mark.CutString(0, true)); + EXPECT_EQ(dangling_mark_text + kEllipsisUTF16, + slicer_dangling_mark.CutString(1, true)); +} + +TEST(TextEliderTest, StringSlicerCombiningSurrogate) { + // The ultimate test: combining sequences comprised of surrogate pairs. + // The following string contains a single combining character sequence: + // MUSICAL SYMBOL G CLEF (U+1D11E) + MUSICAL SYMBOL COMBINING FLAG-1 (U+1D16E) + // Represented as four UTF-16 code units. + const base::char16 kText[] = {0xD834, 0xDD1E, 0xD834, 0xDD6E, 0}; + base::string16 text(kText); + base::string16 ellipsis(kEllipsisUTF16); + StringSlicer slicer(text, ellipsis, false, false); + + // Attempt to cut the string for all lengths. Should always round left and + // exclude the combining sequence. + EXPECT_EQ(base::string16(kEllipsisUTF16), slicer.CutString(0, true)); + EXPECT_EQ(base::string16(kEllipsisUTF16), slicer.CutString(1, true)); + EXPECT_EQ(base::string16(kEllipsisUTF16), slicer.CutString(2, true)); + EXPECT_EQ(base::string16(kEllipsisUTF16), slicer.CutString(3, true)); + EXPECT_EQ(text + kEllipsisUTF16, slicer.CutString(4, true)); + + // Cut string in the middle. Should exclude the sequence. + StringSlicer slicer_mid(text, ellipsis, true, false); + EXPECT_EQ(base::string16(kEllipsisUTF16), slicer_mid.CutString(4, true)); +} + TEST(TextEliderTest, ElideString) { struct TestData { const char* input;
diff --git a/ui/gfx/win/window_impl.cc b/ui/gfx/win/window_impl.cc index 94e315c..f01f182 100644 --- a/ui/gfx/win/window_impl.cc +++ b/ui/gfx/win/window_impl.cc
@@ -41,7 +41,8 @@ // Compares two ClassInfos. Returns true if all members match. bool Equals(const ClassInfo& other) const { - return (other.style == style && other.icon == icon); + return (other.style == style && other.icon == icon && + other.small_icon == small_icon); } };
diff --git a/ui/gl/angle_platform_impl.cc b/ui/gl/angle_platform_impl.cc index 47f6cef..732f088 100644 --- a/ui/gl/angle_platform_impl.cc +++ b/ui/gl/angle_platform_impl.cc
@@ -16,6 +16,10 @@ ANGLEPlatformImpl::~ANGLEPlatformImpl() { } +double ANGLEPlatformImpl::currentTime() { + return base::Time::Now().ToDoubleT(); +} + double ANGLEPlatformImpl::monotonicallyIncreasingTime() { return base::TimeTicks::Now().ToInternalValue() / static_cast<double>(base::Time::kMicrosecondsPerSecond);
diff --git a/ui/gl/angle_platform_impl.h b/ui/gl/angle_platform_impl.h index 92d1397f..4f1aac4b7 100644 --- a/ui/gl/angle_platform_impl.h +++ b/ui/gl/angle_platform_impl.h
@@ -20,6 +20,7 @@ ~ANGLEPlatformImpl() override; // angle::Platform: + double currentTime() override; double monotonicallyIncreasingTime() override; const unsigned char* getTraceCategoryEnabledFlag( const char* category_group) override;
diff --git a/ui/gl/gl_context_wgl.h b/ui/gl/gl_context_wgl.h index d97e568..7a0a437 100644 --- a/ui/gl/gl_context_wgl.h +++ b/ui/gl/gl_context_wgl.h
@@ -18,18 +18,18 @@ class GLContextWGL : public GLContextReal { public: explicit GLContextWGL(GLShareGroup* share_group); - virtual ~GLContextWGL(); + ~GLContextWGL() override; // Implement GLContext. - virtual bool Initialize( - GLSurface* compatible_surface, GpuPreference gpu_preference); - virtual void Destroy(); - virtual bool MakeCurrent(GLSurface* surface); - virtual void ReleaseCurrent(GLSurface* surface); - virtual bool IsCurrent(GLSurface* surface); - virtual void* GetHandle(); - virtual void OnSetSwapInterval(int interval); - virtual std::string GetExtensions(); + bool Initialize(GLSurface* compatible_surface, + GpuPreference gpu_preference) override; + void Destroy() override; + bool MakeCurrent(GLSurface* surface) override; + void ReleaseCurrent(GLSurface* surface) override; + bool IsCurrent(GLSurface* surface) override; + void* GetHandle() override; + void OnSetSwapInterval(int interval) override; + std::string GetExtensions() override; private: HGLRC context_;
diff --git a/ui/gl/gl_surface_wgl.h b/ui/gl/gl_surface_wgl.h index aa32047..b65fad2 100644 --- a/ui/gl/gl_surface_wgl.h +++ b/ui/gl/gl_surface_wgl.h
@@ -14,10 +14,10 @@ class GLSurfaceWGL : public GLSurface { public: GLSurfaceWGL(); - virtual ~GLSurfaceWGL(); + ~GLSurfaceWGL() override; // Implement GLSurface. - virtual void* GetDisplay(); + void* GetDisplay() override; static bool InitializeOneOff(); static HDC GetDisplayDC(); @@ -30,15 +30,15 @@ class NativeViewGLSurfaceWGL : public GLSurfaceWGL { public: explicit NativeViewGLSurfaceWGL(gfx::AcceleratedWidget window); - virtual ~NativeViewGLSurfaceWGL(); + ~NativeViewGLSurfaceWGL() override; // Implement GLSurface. - virtual bool Initialize(); - virtual void Destroy(); - virtual bool IsOffscreen(); - virtual bool SwapBuffers(); - virtual gfx::Size GetSize(); - virtual void* GetHandle(); + bool Initialize() override; + void Destroy() override; + bool IsOffscreen() override; + bool SwapBuffers() override; + gfx::Size GetSize() override; + void* GetHandle() override; private: gfx::AcceleratedWidget window_; @@ -53,15 +53,15 @@ class PbufferGLSurfaceWGL : public GLSurfaceWGL { public: explicit PbufferGLSurfaceWGL(const gfx::Size& size); - virtual ~PbufferGLSurfaceWGL(); + ~PbufferGLSurfaceWGL() override; // Implement GLSurface. - virtual bool Initialize(); - virtual void Destroy(); - virtual bool IsOffscreen(); - virtual bool SwapBuffers(); - virtual gfx::Size GetSize(); - virtual void* GetHandle(); + bool Initialize() override; + void Destroy() override; + bool IsOffscreen() override; + bool SwapBuffers() override; + gfx::Size GetSize() override; + void* GetHandle() override; private: gfx::Size size_;
diff --git a/ui/gl/gl_surface_win.cc b/ui/gl/gl_surface_win.cc index ed2958e8..8d55009 100644 --- a/ui/gl/gl_surface_win.cc +++ b/ui/gl/gl_surface_win.cc
@@ -33,15 +33,15 @@ class NativeViewGLSurfaceOSMesa : public GLSurfaceOSMesa { public: explicit NativeViewGLSurfaceOSMesa(gfx::AcceleratedWidget window); - virtual ~NativeViewGLSurfaceOSMesa(); + ~NativeViewGLSurfaceOSMesa() override; // Implement subset of GLSurface. - virtual bool Initialize() override; - virtual void Destroy() override; - virtual bool IsOffscreen() override; - virtual bool SwapBuffers() override; - virtual bool SupportsPostSubBuffer() override; - virtual bool PostSubBuffer(int x, int y, int width, int height) override; + bool Initialize() override; + void Destroy() override; + bool IsOffscreen() override; + bool SwapBuffers() override; + bool SupportsPostSubBuffer() override; + bool PostSubBuffer(int x, int y, int width, int height) override; private: gfx::AcceleratedWidget window_; @@ -58,9 +58,9 @@ use_dwm_ = (base::win::GetVersion() >= base::win::VERSION_WIN7); } - virtual ~WinVSyncProvider() {} + ~WinVSyncProvider() override {} - virtual void GetVSyncParameters(const UpdateVSyncCallback& callback) { + void GetVSyncParameters(const UpdateVSyncCallback& callback) override { TRACE_EVENT0("gpu", "WinVSyncProvider::GetVSyncParameters"); base::TimeTicks timebase;
diff --git a/ui/gl/gl_wgl_api_implementation.h b/ui/gl/gl_wgl_api_implementation.h index dbef51a..b4da117 100644 --- a/ui/gl/gl_wgl_api_implementation.h +++ b/ui/gl/gl_wgl_api_implementation.h
@@ -28,7 +28,7 @@ protected: WGLApiBase(); - virtual ~WGLApiBase(); + ~WGLApiBase() override; void InitializeBase(DriverWGL* driver); DriverWGL* driver_; @@ -37,7 +37,7 @@ class GL_EXPORT RealWGLApi : public WGLApiBase { public: RealWGLApi(); - virtual ~RealWGLApi(); + ~RealWGLApi() override; void Initialize(DriverWGL* driver); }; @@ -45,7 +45,7 @@ class GL_EXPORT TraceWGLApi : public WGLApi { public: TraceWGLApi(WGLApi* wgl_api) : wgl_api_(wgl_api) { } - virtual ~TraceWGLApi(); + ~TraceWGLApi() override; // Include the auto-generated part of this class. We split this because // it means we can easily edit the non-auto generated parts right here in
diff --git a/ui/keyboard/BUILD.gn b/ui/keyboard/BUILD.gn index 28f34f1..8f7c8a3a 100644 --- a/ui/keyboard/BUILD.gn +++ b/ui/keyboard/BUILD.gn
@@ -43,6 +43,7 @@ "//ui/compositor", "//ui/events", "//ui/events:dom4_keycode_converter", + "//ui/events:events_base", "//ui/gfx", "//ui/gfx/geometry", "//ui/wm",
diff --git a/ui/keyboard/keyboard.gyp b/ui/keyboard/keyboard.gyp index 65709084..18069fa7 100644 --- a/ui/keyboard/keyboard.gyp +++ b/ui/keyboard/keyboard.gyp
@@ -59,6 +59,7 @@ '../compositor/compositor.gyp:compositor', '../events/events.gyp:dom4_keycode_converter', '../events/events.gyp:events', + '../events/events.gyp:events_base', '../gfx/gfx.gyp:gfx', '../gfx/gfx.gyp:gfx_geometry', '../wm/wm.gyp:wm',
diff --git a/ui/keyboard/keyboard_util.cc b/ui/keyboard/keyboard_util.cc index c234127..7b77b438f 100644 --- a/ui/keyboard/keyboard_util.cc +++ b/ui/keyboard/keyboard_util.cc
@@ -6,6 +6,7 @@ #include <string> +#include "base/basictypes.h" #include "base/command_line.h" #include "base/lazy_instance.h" #include "base/logging.h" @@ -18,7 +19,11 @@ #include "ui/base/ime/input_method.h" #include "ui/base/ime/text_input_client.h" #include "ui/events/event_processor.h" +#include "ui/events/event_utils.h" +#include "ui/events/keycodes/dom3/dom_code.h" +#include "ui/events/keycodes/dom3/dom_key.h" #include "ui/events/keycodes/dom4/keycode_converter.h" +#include "ui/events/keycodes/keyboard_code_conversion.h" #include "ui/keyboard/keyboard_controller.h" #include "ui/keyboard/keyboard_controller_proxy.h" #include "ui/keyboard/keyboard_switches.h" @@ -31,7 +36,8 @@ void SendProcessKeyEvent(ui::EventType type, aura::WindowTreeHost* host) { - ui::KeyEvent event(type, ui::VKEY_PROCESSKEY, ui::EF_NONE); + ui::KeyEvent event(type, ui::VKEY_PROCESSKEY, ui::DomCode::NONE, ui::EF_NONE, + ui::DomKey::PROCESS, 0, ui::EventTimeForNow()); event.SetTranslated(true); ui::EventDispatchDetails details = host->event_processor()->OnEventFromSource(&event); @@ -191,36 +197,54 @@ aura::WindowTreeHost* host) { if (!host) return false; - ui::KeyboardCode codex = ui::VKEY_UNKNOWN; - ui::KeyboardCode codey = ui::VKEY_UNKNOWN; + ui::DomCode domcodex = ui::DomCode::NONE; + ui::DomCode domcodey = ui::DomCode::NONE; if (swipe_direction & kCursorMoveRight) - codex = ui::VKEY_RIGHT; + domcodex = ui::DomCode::ARROW_RIGHT; else if (swipe_direction & kCursorMoveLeft) - codex = ui::VKEY_LEFT; + domcodex = ui::DomCode::ARROW_LEFT; if (swipe_direction & kCursorMoveUp) - codey = ui::VKEY_UP; + domcodey = ui::DomCode::ARROW_UP; else if (swipe_direction & kCursorMoveDown) - codey = ui::VKEY_DOWN; + domcodey = ui::DomCode::ARROW_DOWN; // First deal with the x movement. - if (codex != ui::VKEY_UNKNOWN) { - ui::KeyEvent press_event(ui::ET_KEY_PRESSED, codex, modifier_flags); + if (domcodex != ui::DomCode::NONE) { + ui::KeyboardCode codex = ui::VKEY_UNKNOWN; + ui::DomKey domkeyx = ui::DomKey::NONE; + base::char16 cx; + ignore_result(DomCodeToUsLayoutMeaning(domcodex, ui::EF_NONE, &domkeyx, + &cx, &codex)); + ui::KeyEvent press_event(ui::ET_KEY_PRESSED, codex, domcodex, + modifier_flags, domkeyx, cx, + ui::EventTimeForNow()); ui::EventDispatchDetails details = host->event_processor()->OnEventFromSource(&press_event); CHECK(!details.dispatcher_destroyed); - ui::KeyEvent release_event(ui::ET_KEY_RELEASED, codex, modifier_flags); + ui::KeyEvent release_event(ui::ET_KEY_RELEASED, codex, domcodex, + modifier_flags, domkeyx, cx, + ui::EventTimeForNow()); details = host->event_processor()->OnEventFromSource(&release_event); CHECK(!details.dispatcher_destroyed); } // Then deal with the y movement. - if (codey != ui::VKEY_UNKNOWN) { - ui::KeyEvent press_event(ui::ET_KEY_PRESSED, codey, modifier_flags); + if (domcodey != ui::DomCode::NONE) { + ui::KeyboardCode codey = ui::VKEY_UNKNOWN; + ui::DomKey domkeyy = ui::DomKey::NONE; + base::char16 cy; + ignore_result(DomCodeToUsLayoutMeaning(domcodey, ui::EF_NONE, &domkeyy, + &cy, &codey)); + ui::KeyEvent press_event(ui::ET_KEY_PRESSED, codey, domcodey, + modifier_flags, domkeyy, cy, + ui::EventTimeForNow()); ui::EventDispatchDetails details = host->event_processor()->OnEventFromSource(&press_event); CHECK(!details.dispatcher_destroyed); - ui::KeyEvent release_event(ui::ET_KEY_RELEASED, codey, modifier_flags); + ui::KeyEvent release_event(ui::ET_KEY_RELEASED, codey, domcodey, + modifier_flags, domkeyy, cy, + ui::EventTimeForNow()); details = host->event_processor()->OnEventFromSource(&release_event); CHECK(!details.dispatcher_destroyed); } @@ -274,10 +298,16 @@ } } + ui::DomCode dom_code = ui::DomCode::NONE; + if (!key_name.empty()) + dom_code = ui::KeycodeConverter::CodeStringToDomCode(key_name.c_str()); + if (dom_code == ui::DomCode::NONE) + dom_code = ui::UsLayoutKeyboardCodeToDomCode(code); + CHECK(dom_code != ui::DomCode::NONE); ui::KeyEvent event( event_type, code, - ui::KeycodeConverter::CodeStringToDomCode(key_name.c_str()), + dom_code, modifiers); ui::EventDispatchDetails details = host->event_processor()->OnEventFromSource(&event);
diff --git a/ui/login/display_manager.js b/ui/login/display_manager.js index 62beda45..958d72fc 100644 --- a/ui/login/display_manager.js +++ b/ui/login/display_manager.js
@@ -52,6 +52,7 @@ 'app_launch_network_config'; /** @const */ var ACCELERATOR_NEW_OOBE = 'new_oobe'; /** @const */ var ACCELERATOR_TOGGLE_WEBVIEW_SIGNIN = 'toggle_webview_signin'; +/** @const */ var ACCELERATOR_TOGGLE_NEW_LOGIN_UI = 'toggle_new_login_ui'; /* Signin UI state constants. Used to control header bar UI. */ /** @const */ var SIGNIN_UI_STATE = { @@ -408,6 +409,9 @@ if (currentStepId == SCREEN_GAIA_SIGNIN || currentStepId == SCREEN_OOBE_ENROLLMENT) chrome.send('toggleWebviewSignin'); + } else if (name == ACCELERATOR_TOGGLE_NEW_LOGIN_UI) { + if (currentStepId == SCREEN_OOBE_NETWORK) + chrome.send('toggleNewLoginUI'); } else if (name == ACCELERATOR_TOGGLE_EASY_BOOTSTRAP) { if (currentStepId == SCREEN_GAIA_SIGNIN) chrome.send('toggleEasyBootstrap');
diff --git a/ui/native_theme/native_theme_aurawin.h b/ui/native_theme/native_theme_aurawin.h index 7c7e57d..11bb87d2 100644 --- a/ui/native_theme/native_theme_aurawin.h +++ b/ui/native_theme/native_theme_aurawin.h
@@ -18,17 +18,17 @@ private: NativeThemeAuraWin(); - virtual ~NativeThemeAuraWin(); + ~NativeThemeAuraWin() override; // Overridden from NativeThemeBase: - virtual gfx::Size GetPartSize(Part part, - State state, - const ExtraParams& extra) const override; - virtual void Paint(SkCanvas* canvas, - Part part, - State state, - const gfx::Rect& rect, - const ExtraParams& extra) const override; + gfx::Size GetPartSize(Part part, + State state, + const ExtraParams& extra) const override; + void Paint(SkCanvas* canvas, + Part part, + State state, + const gfx::Rect& rect, + const ExtraParams& extra) const override; DISALLOW_COPY_AND_ASSIGN(NativeThemeAuraWin); };
diff --git a/ui/native_theme/native_theme_win.h b/ui/native_theme/native_theme_win.h index 24b718f..46b62ae 100644 --- a/ui/native_theme/native_theme_win.h +++ b/ui/native_theme/native_theme_win.h
@@ -104,22 +104,22 @@ bool draw_edges) const; // NativeTheme implementation: - virtual gfx::Size GetPartSize(Part part, - State state, - const ExtraParams& extra) const override; - virtual void Paint(SkCanvas* canvas, - Part part, - State state, - const gfx::Rect& rect, - const ExtraParams& extra) const override; - virtual SkColor GetSystemColor(ColorId color_id) const override; + gfx::Size GetPartSize(Part part, + State state, + const ExtraParams& extra) const override; + void Paint(SkCanvas* canvas, + Part part, + State state, + const gfx::Rect& rect, + const ExtraParams& extra) const override; + SkColor GetSystemColor(ColorId color_id) const override; private: NativeThemeWin(); - ~NativeThemeWin(); + ~NativeThemeWin() override; // gfx::SysColorChangeListener implementation: - virtual void OnSysColorChange() override; + void OnSysColorChange() override; // Update the locally cached set of system colors. void UpdateSystemColors();
diff --git a/ui/ozone/common/native_display_delegate_ozone.cc b/ui/ozone/common/native_display_delegate_ozone.cc index 7f7e15d..0aa8e33 100644 --- a/ui/ozone/common/native_display_delegate_ozone.cc +++ b/ui/ozone/common/native_display_delegate_ozone.cc
@@ -76,18 +76,33 @@ NOTIMPLEMENTED(); } -bool NativeDisplayDelegateOzone::GetHDCPState(const ui::DisplaySnapshot& output, - ui::HDCPState* state) { +bool NativeDisplayDelegateOzone::GetHDCPState(const DisplaySnapshot& output, + HDCPState* state) { NOTIMPLEMENTED(); return false; } -bool NativeDisplayDelegateOzone::SetHDCPState(const ui::DisplaySnapshot& output, - ui::HDCPState state) { +bool NativeDisplayDelegateOzone::SetHDCPState(const DisplaySnapshot& output, + HDCPState state) { NOTIMPLEMENTED(); return false; } +void NativeDisplayDelegateOzone::GetHDCPState( + const ui::DisplaySnapshot& output, + const GetHDCPStateCallback& callback) { + NOTIMPLEMENTED(); + callback.Run(false, HDCP_STATE_UNDESIRED); +} + +void NativeDisplayDelegateOzone::SetHDCPState( + const ui::DisplaySnapshot& output, + ui::HDCPState state, + const SetHDCPStateCallback& callback) { + NOTIMPLEMENTED(); + callback.Run(false); +} + std::vector<ui::ColorCalibrationProfile> NativeDisplayDelegateOzone::GetAvailableColorCalibrationProfiles( const ui::DisplaySnapshot& output) {
diff --git a/ui/ozone/common/native_display_delegate_ozone.h b/ui/ozone/common/native_display_delegate_ozone.h index 3801834..a9e17dd 100644 --- a/ui/ozone/common/native_display_delegate_ozone.h +++ b/ui/ozone/common/native_display_delegate_ozone.h
@@ -33,10 +33,13 @@ const gfx::Point& origin, const ConfigureCallback& callback) override; void CreateFrameBuffer(const gfx::Size& size) override; - bool GetHDCPState(const ui::DisplaySnapshot& output, - ui::HDCPState* state) override; - bool SetHDCPState(const ui::DisplaySnapshot& output, - ui::HDCPState state) override; + bool GetHDCPState(const DisplaySnapshot& output, HDCPState* state) override; + bool SetHDCPState(const DisplaySnapshot& output, HDCPState state) override; + void GetHDCPState(const ui::DisplaySnapshot& output, + const GetHDCPStateCallback& callback) override; + void SetHDCPState(const ui::DisplaySnapshot& output, + ui::HDCPState state, + const SetHDCPStateCallback& callback) override; std::vector<ui::ColorCalibrationProfile> GetAvailableColorCalibrationProfiles( const ui::DisplaySnapshot& output) override; bool SetColorCalibrationProfile(
diff --git a/ui/ozone/platform/drm/gpu/drm_buffer.cc b/ui/ozone/platform/drm/gpu/drm_buffer.cc index bdf5727..e9ef7c4 100644 --- a/ui/ozone/platform/drm/gpu/drm_buffer.cc +++ b/ui/ozone/platform/drm/gpu/drm_buffer.cc
@@ -35,29 +35,38 @@ } // namespace DrmBuffer::DrmBuffer(const scoped_refptr<DrmDevice>& drm) - : drm_(drm), handle_(0), framebuffer_(0) { + : drm_(drm), + stride_(0), + handle_(0), + mmap_base_(0), + mmap_size_(0), + framebuffer_(0) { } DrmBuffer::~DrmBuffer() { - if (!surface_) - return; + surface_.clear(); - if (framebuffer_) - drm_->RemoveFramebuffer(framebuffer_); + if (framebuffer_ && !drm_->RemoveFramebuffer(framebuffer_)) + PLOG(ERROR) << "DrmBuffer: RemoveFramebuffer: fb " << framebuffer_; - SkImageInfo info; - void* pixels = const_cast<void*>(surface_->peekPixels(&info, NULL)); - if (!pixels) - return; + if (mmap_base_ && !drm_->UnmapDumbBuffer(mmap_base_, mmap_size_)) + PLOG(ERROR) << "DrmBuffer: UnmapDumbBuffer: handle " << handle_; - drm_->DestroyDumbBuffer(info, handle_, stride_, pixels); + if (handle_ && !drm_->DestroyDumbBuffer(handle_)) + PLOG(ERROR) << "DrmBuffer: DestroyDumbBuffer: handle " << handle_; } bool DrmBuffer::Initialize(const SkImageInfo& info, bool should_register_framebuffer) { - void* pixels = NULL; - if (!drm_->CreateDumbBuffer(info, &handle_, &stride_, &pixels)) { - VLOG(2) << "Cannot create drm dumb buffer"; + if (!drm_->CreateDumbBuffer(info, &handle_, &stride_)) { + PLOG(ERROR) << "DrmBuffer: CreateDumbBuffer: width " << info.width() + << " height " << info.height(); + return false; + } + + mmap_size_ = info.getSafeSize(stride_); + if (!drm_->MapDumbBuffer(handle_, mmap_size_, &mmap_base_)) { + PLOG(ERROR) << "DrmBuffer: MapDumbBuffer: handle " << handle_; return false; } @@ -65,13 +74,14 @@ !drm_->AddFramebuffer( info.width(), info.height(), GetColorDepth(info.colorType()), info.bytesPerPixel() << 3, stride_, handle_, &framebuffer_)) { - VPLOG(2) << "Failed to register framebuffer"; + PLOG(ERROR) << "DrmBuffer: AddFramebuffer: handle " << handle_; return false; } - surface_ = skia::AdoptRef(SkSurface::NewRasterDirect(info, pixels, stride_)); + surface_ = + skia::AdoptRef(SkSurface::NewRasterDirect(info, mmap_base_, stride_)); if (!surface_) { - VLOG(2) << "Cannot install Skia pixels for drm buffer"; + LOG(ERROR) << "DrmBuffer: Failed to create SkSurface: handle " << handle_; return false; }
diff --git a/ui/ozone/platform/drm/gpu/drm_buffer.h b/ui/ozone/platform/drm/gpu/drm_buffer.h index 18ad9af..6dcd02b 100644 --- a/ui/ozone/platform/drm/gpu/drm_buffer.h +++ b/ui/ozone/platform/drm/gpu/drm_buffer.h
@@ -40,19 +40,25 @@ scoped_refptr<DrmDevice> drm_; - // Wrapper around the native pixel memory. - skia::RefPtr<SkSurface> surface_; - // Length of a row of pixels. uint32_t stride_; // Buffer handle used by the DRM allocator. uint32_t handle_; + // Base address for memory mapping. + void* mmap_base_; + + // Size for memory mapping. + size_t mmap_size_; + // Buffer ID used by the DRM modesettings API. This is set when the buffer is // registered with the CRTC. uint32_t framebuffer_; + // Wrapper around the native pixel memory. + skia::RefPtr<SkSurface> surface_; + DISALLOW_COPY_AND_ASSIGN(DrmBuffer); };
diff --git a/ui/ozone/platform/drm/gpu/drm_console_buffer.cc b/ui/ozone/platform/drm/gpu/drm_console_buffer.cc index 2244fc2..871c2fa0 100644 --- a/ui/ozone/platform/drm/gpu/drm_console_buffer.cc +++ b/ui/ozone/platform/drm/gpu/drm_console_buffer.cc
@@ -9,7 +9,6 @@ #include "third_party/skia/include/core/SkCanvas.h" #include "ui/ozone/platform/drm/gpu/drm_device.h" -#include "ui/ozone/platform/drm/gpu/drm_util.h" #include "ui/ozone/platform/drm/gpu/scoped_drm_types.h" namespace ui { @@ -44,7 +43,7 @@ mmap_size_ = info.getSafeSize(stride_); - if (!MapDumbBuffer(drm_->get_fd(), fb->handle, mmap_size_, &mmap_base_)) { + if (!drm_->MapDumbBuffer(fb->handle, mmap_size_, &mmap_base_)) { mmap_base_ = NULL; return false; }
diff --git a/ui/ozone/platform/drm/gpu/drm_device.cc b/ui/ozone/platform/drm/gpu/drm_device.cc index b547b08..7b03b43 100644 --- a/ui/ozone/platform/drm/gpu/drm_device.cc +++ b/ui/ozone/platform/drm/gpu/drm_device.cc
@@ -66,11 +66,11 @@ return true; } -void DrmDestroyDumbBuffer(int fd, uint32_t handle) { +bool DrmDestroyDumbBuffer(int fd, uint32_t handle) { struct drm_mode_destroy_dumb destroy_request; memset(&destroy_request, 0, sizeof(destroy_request)); destroy_request.handle = handle; - drmIoctl(fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy_request); + return !drmIoctl(fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy_request); } void HandlePageFlipEventOnIO(int fd, @@ -451,31 +451,41 @@ bool DrmDevice::CreateDumbBuffer(const SkImageInfo& info, uint32_t* handle, - uint32_t* stride, - void** pixels) { + uint32_t* stride) { DCHECK(file_.IsValid()); TRACE_EVENT0("drm", "DrmDevice::CreateDumbBuffer"); - if (!DrmCreateDumbBuffer(file_.GetPlatformFile(), info, handle, stride)) - return false; + return DrmCreateDumbBuffer(file_.GetPlatformFile(), info, handle, stride); +} - if (!MapDumbBuffer(file_.GetPlatformFile(), *handle, - info.getSafeSize(*stride), pixels)) { - DrmDestroyDumbBuffer(file_.GetPlatformFile(), *handle); +bool DrmDevice::DestroyDumbBuffer(uint32_t handle) { + DCHECK(file_.IsValid()); + TRACE_EVENT1("drm", "DrmDevice::DestroyDumbBuffer", "handle", handle); + return DrmDestroyDumbBuffer(file_.GetPlatformFile(), handle); +} + +bool DrmDevice::MapDumbBuffer(uint32_t handle, size_t size, void** pixels) { + struct drm_mode_map_dumb map_request; + memset(&map_request, 0, sizeof(map_request)); + map_request.handle = handle; + if (drmIoctl(file_.GetPlatformFile(), DRM_IOCTL_MODE_MAP_DUMB, + &map_request)) { + PLOG(ERROR) << "Cannot prepare dumb buffer for mapping"; + return false; + } + + *pixels = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, + file_.GetPlatformFile(), map_request.offset); + if (*pixels == MAP_FAILED) { + PLOG(ERROR) << "Cannot mmap dumb buffer"; return false; } return true; } -void DrmDevice::DestroyDumbBuffer(const SkImageInfo& info, - uint32_t handle, - uint32_t stride, - void* pixels) { - DCHECK(file_.IsValid()); - TRACE_EVENT1("drm", "DrmDevice::DestroyDumbBuffer", "handle", handle); - munmap(pixels, info.getSafeSize(stride)); - DrmDestroyDumbBuffer(file_.GetPlatformFile(), handle); +bool DrmDevice::UnmapDumbBuffer(void* pixels, size_t size) { + return !munmap(pixels, size); } bool DrmDevice::CloseBufferHandle(uint32_t handle) {
diff --git a/ui/ozone/platform/drm/gpu/drm_device.h b/ui/ozone/platform/drm/gpu/drm_device.h index 67ffcffa..6fc2e49 100644 --- a/ui/ozone/platform/drm/gpu/drm_device.h +++ b/ui/ozone/platform/drm/gpu/drm_device.h
@@ -146,13 +146,13 @@ virtual bool CreateDumbBuffer(const SkImageInfo& info, uint32_t* handle, - uint32_t* stride, - void** pixels); + uint32_t* stride); - virtual void DestroyDumbBuffer(const SkImageInfo& info, - uint32_t handle, - uint32_t stride, - void* pixels); + virtual bool DestroyDumbBuffer(uint32_t handle); + + virtual bool MapDumbBuffer(uint32_t handle, size_t size, void** pixels); + + virtual bool UnmapDumbBuffer(void* pixels, size_t size); virtual bool CloseBufferHandle(uint32_t handle);
diff --git a/ui/ozone/platform/drm/gpu/drm_util.cc b/ui/ozone/platform/drm/gpu/drm_util.cc index 517a58c..67f3c94 100644 --- a/ui/ozone/platform/drm/gpu/drm_util.cc +++ b/ui/ozone/platform/drm/gpu/drm_util.cc
@@ -108,25 +108,6 @@ lhs.flags == rhs.flags && strcmp(lhs.name, rhs.name) == 0; } -bool MapDumbBuffer(int fd, uint32_t handle, uint32_t size, void** pixels) { - struct drm_mode_map_dumb map_request; - memset(&map_request, 0, sizeof(map_request)); - map_request.handle = handle; - if (drmIoctl(fd, DRM_IOCTL_MODE_MAP_DUMB, &map_request)) { - VPLOG(2) << "Cannot prepare dumb buffer for mapping"; - return false; - } - - *pixels = - mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, map_request.offset); - if (*pixels == MAP_FAILED) { - VPLOG(2) << "Cannot mmap dumb buffer"; - return false; - } - - return true; -} - void ForceInitializationOfPrimaryDisplay(const scoped_refptr<DrmDevice>& drm, ScreenManager* screen_manager) { VLOG(2) << "Forcing initialization of primary display.";
diff --git a/ui/ozone/platform/drm/gpu/drm_util.h b/ui/ozone/platform/drm/gpu/drm_util.h index a15a60f7..baf8893 100644 --- a/ui/ozone/platform/drm/gpu/drm_util.h +++ b/ui/ozone/platform/drm/gpu/drm_util.h
@@ -43,9 +43,6 @@ bool SameMode(const drmModeModeInfo& lhs, const drmModeModeInfo& rhs); -// Memory maps a DRM buffer. -bool MapDumbBuffer(int fd, uint32_t handle, uint32_t size, void** pixels); - void ForceInitializationOfPrimaryDisplay(const scoped_refptr<DrmDevice>& drm, ScreenManager* screen_manager);
diff --git a/ui/ozone/platform/drm/host/drm_native_display_delegate.cc b/ui/ozone/platform/drm/host/drm_native_display_delegate.cc index ec84b0a..0a2a4c2 100644 --- a/ui/ozone/platform/drm/host/drm_native_display_delegate.cc +++ b/ui/ozone/platform/drm/host/drm_native_display_delegate.cc
@@ -196,6 +196,21 @@ return false; } +void DrmNativeDisplayDelegate::GetHDCPState( + const ui::DisplaySnapshot& output, + const GetHDCPStateCallback& callback) { + NOTIMPLEMENTED(); + callback.Run(false, HDCP_STATE_UNDESIRED); +} + +void DrmNativeDisplayDelegate::SetHDCPState( + const ui::DisplaySnapshot& output, + ui::HDCPState state, + const SetHDCPStateCallback& callback) { + NOTIMPLEMENTED(); + callback.Run(false); +} + std::vector<ColorCalibrationProfile> DrmNativeDisplayDelegate::GetAvailableColorCalibrationProfiles( const DisplaySnapshot& output) {
diff --git a/ui/ozone/platform/drm/host/drm_native_display_delegate.h b/ui/ozone/platform/drm/host/drm_native_display_delegate.h index 42802a8..986f28a 100644 --- a/ui/ozone/platform/drm/host/drm_native_display_delegate.h +++ b/ui/ozone/platform/drm/host/drm_native_display_delegate.h
@@ -52,6 +52,11 @@ void CreateFrameBuffer(const gfx::Size& size) override; bool GetHDCPState(const DisplaySnapshot& output, HDCPState* state) override; bool SetHDCPState(const DisplaySnapshot& output, HDCPState state) override; + void GetHDCPState(const DisplaySnapshot& output, + const GetHDCPStateCallback& callback) override; + void SetHDCPState(const DisplaySnapshot& output, + HDCPState state, + const SetHDCPStateCallback& callback) override; std::vector<ColorCalibrationProfile> GetAvailableColorCalibrationProfiles( const DisplaySnapshot& output) override; bool SetColorCalibrationProfile(const DisplaySnapshot& output,
diff --git a/ui/ozone/platform/drm/test/mock_drm_device.cc b/ui/ozone/platform/drm/test/mock_drm_device.cc index eb11623..3a822dc 100644 --- a/ui/ozone/platform/drm/test/mock_drm_device.cc +++ b/ui/ozone/platform/drm/test/mock_drm_device.cc
@@ -196,27 +196,38 @@ bool MockDrmDevice::CreateDumbBuffer(const SkImageInfo& info, uint32_t* handle, - uint32_t* stride, - void** pixels) { - allocate_buffer_count_++; + uint32_t* stride) { if (!create_dumb_buffer_expectation_) return false; - *handle = allocate_buffer_count_; + *handle = allocate_buffer_count_++; *stride = info.minRowBytes(); - *pixels = new char[info.getSafeSize(*stride)]; + void* pixels = new char[info.getSafeSize(*stride)]; buffers_.push_back( - skia::AdoptRef(SkSurface::NewRasterDirect(info, *pixels, *stride))); - buffers_.back()->getCanvas()->clear(SK_ColorBLACK); + skia::AdoptRef(SkSurface::NewRasterDirect(info, pixels, *stride))); + buffers_[*handle]->getCanvas()->clear(SK_ColorBLACK); return true; } -void MockDrmDevice::DestroyDumbBuffer(const SkImageInfo& info, - uint32_t handle, - uint32_t stride, - void* pixels) { - delete[] static_cast<char*>(pixels); +bool MockDrmDevice::DestroyDumbBuffer(uint32_t handle) { + if (handle >= buffers_.size() || !buffers_[handle]) + return false; + + buffers_[handle].clear(); + return true; +} + +bool MockDrmDevice::MapDumbBuffer(uint32_t handle, size_t size, void** pixels) { + if (handle >= buffers_.size() || !buffers_[handle]) + return false; + + *pixels = const_cast<void*>(buffers_[handle]->peekPixels(nullptr, nullptr)); + return true; +} + +bool MockDrmDevice::UnmapDumbBuffer(void* pixels, size_t size) { + return true; } bool MockDrmDevice::CloseBufferHandle(uint32_t handle) {
diff --git a/ui/ozone/platform/drm/test/mock_drm_device.h b/ui/ozone/platform/drm/test/mock_drm_device.h index 41d5ba3..5632b6e2 100644 --- a/ui/ozone/platform/drm/test/mock_drm_device.h +++ b/ui/ozone/platform/drm/test/mock_drm_device.h
@@ -100,13 +100,10 @@ bool MoveCursor(uint32_t crtc_id, const gfx::Point& point) override; bool CreateDumbBuffer(const SkImageInfo& info, uint32_t* handle, - uint32_t* stride, - void** pixels) override; - void DestroyDumbBuffer(const SkImageInfo& info, - uint32_t handle, - uint32_t stride, - void* pixels) override; - + uint32_t* stride) override; + bool DestroyDumbBuffer(uint32_t handle) override; + bool MapDumbBuffer(uint32_t handle, size_t size, void** pixels) override; + bool UnmapDumbBuffer(void* pixels, size_t size) override; bool CloseBufferHandle(uint32_t handle) override; bool CommitProperties(drmModePropertySet* properties, uint32_t flags,
diff --git a/ui/platform_window/win/win_window.h b/ui/platform_window/win/win_window.h index 12aa7ad..8fb8adc 100644 --- a/ui/platform_window/win/win_window.h +++ b/ui/platform_window/win/win_window.h
@@ -16,26 +16,26 @@ public gfx::WindowImpl { public: WinWindow(PlatformWindowDelegate* delegate, const gfx::Rect& bounds); - virtual ~WinWindow(); + ~WinWindow() override; private: void Destroy(); // PlatformWindow: - virtual void Show() override; - virtual void Hide() override; - virtual void Close() override; - virtual void SetBounds(const gfx::Rect& bounds) override; - virtual gfx::Rect GetBounds() override; - virtual void SetCapture() override; - virtual void ReleaseCapture() override; - virtual void ToggleFullscreen() override; - virtual void Maximize() override; - virtual void Minimize() override; - virtual void Restore() override; - virtual void SetCursor(PlatformCursor cursor) override; - virtual void MoveCursorTo(const gfx::Point& location) override; - virtual void ConfineCursorToBounds(const gfx::Rect& bounds) override; + void Show() override; + void Hide() override; + void Close() override; + void SetBounds(const gfx::Rect& bounds) override; + gfx::Rect GetBounds() override; + void SetCapture() override; + void ReleaseCapture() override; + void ToggleFullscreen() override; + void Maximize() override; + void Minimize() override; + void Restore() override; + void SetCursor(PlatformCursor cursor) override; + void MoveCursorTo(const gfx::Point& location) override; + void ConfineCursorToBounds(const gfx::Rect& bounds) override; CR_BEGIN_MSG_MAP_EX(WinWindow) CR_MESSAGE_RANGE_HANDLER_EX(WM_MOUSEFIRST, WM_MOUSELAST, OnMouseRange)
diff --git a/ui/views/accessibility/native_view_accessibility_win.h b/ui/views/accessibility/native_view_accessibility_win.h index 087d50e7..dadbcba7 100644 --- a/ui/views/accessibility/native_view_accessibility_win.h +++ b/ui/views/accessibility/native_view_accessibility_win.h
@@ -13,7 +13,7 @@ class NativeViewAccessibilityWin : public NativeViewAccessibility { public: NativeViewAccessibilityWin(View* view); - virtual ~NativeViewAccessibilityWin(); + ~NativeViewAccessibilityWin() override; // NativeViewAccessibility. gfx::NativeViewAccessible GetParent() override;
diff --git a/ui/views/accessibility/native_view_accessibility_win_unittest.cc b/ui/views/accessibility/native_view_accessibility_win_unittest.cc index 2eba9cb..3b955b2 100644 --- a/ui/views/accessibility/native_view_accessibility_win_unittest.cc +++ b/ui/views/accessibility/native_view_accessibility_win_unittest.cc
@@ -22,7 +22,7 @@ class NativeViewAcccessibilityWinTest : public ViewsTestBase { public: NativeViewAcccessibilityWinTest() {} - virtual ~NativeViewAcccessibilityWinTest() {} + ~NativeViewAcccessibilityWinTest() override {} protected: void GetIAccessible2InterfaceForView(View* view, IAccessible2_2** result) {
diff --git a/ui/views/controls/menu/menu_message_pump_dispatcher_win.h b/ui/views/controls/menu/menu_message_pump_dispatcher_win.h index 9f84171b..afc54fe 100644 --- a/ui/views/controls/menu/menu_message_pump_dispatcher_win.h +++ b/ui/views/controls/menu/menu_message_pump_dispatcher_win.h
@@ -19,11 +19,11 @@ class MenuMessagePumpDispatcher : public base::MessagePumpDispatcher { public: explicit MenuMessagePumpDispatcher(MenuController* menu_controller); - virtual ~MenuMessagePumpDispatcher(); + ~MenuMessagePumpDispatcher() override; private: // base::MessagePumpDispatcher: - virtual uint32_t Dispatch(const base::NativeEvent& event) override; + uint32_t Dispatch(const base::NativeEvent& event) override; MenuController* menu_controller_;
diff --git a/ui/views/controls/menu/native_menu_win.h b/ui/views/controls/menu/native_menu_win.h index 39dc37e20..7d4dea9a 100644 --- a/ui/views/controls/menu/native_menu_win.h +++ b/ui/views/controls/menu/native_menu_win.h
@@ -30,18 +30,18 @@ // is non-NULL, the NativeMenuWin wraps the system menu for that window. // The caller owns the model and the delegate. NativeMenuWin(ui::MenuModel* model, HWND system_menu_for); - virtual ~NativeMenuWin(); + ~NativeMenuWin() override; // Overridden from MenuWrapper: - virtual void RunMenuAt(const gfx::Point& point, int alignment) override; - virtual void CancelMenu() override; - virtual void Rebuild(MenuInsertionDelegateWin* delegate) override; - virtual void UpdateStates() override; - virtual HMENU GetNativeMenu() const override; - virtual MenuAction GetMenuAction() const override; - virtual void AddMenuListener(MenuListener* listener) override; - virtual void RemoveMenuListener(MenuListener* listener) override; - virtual void SetMinimumWidth(int width) override; + void RunMenuAt(const gfx::Point& point, int alignment) override; + void CancelMenu() override; + void Rebuild(MenuInsertionDelegateWin* delegate) override; + void UpdateStates() override; + HMENU GetNativeMenu() const override; + MenuAction GetMenuAction() const override; + void AddMenuListener(MenuListener* listener) override; + void RemoveMenuListener(MenuListener* listener) override; + void SetMinimumWidth(int width) override; private: // IMPORTANT: Note about indices.
diff --git a/ui/views/controls/throbber.cc b/ui/views/controls/throbber.cc index d1d5c1d..30cfb3b3 100644 --- a/ui/views/controls/throbber.cc +++ b/ui/views/controls/throbber.cc
@@ -111,7 +111,7 @@ void SmoothedThrobber::Start() { stop_timer_.Stop(); - if (!running_ && !start_timer_.IsRunning()) { + if (!running() && !start_timer_.IsRunning()) { start_timer_.Start(FROM_HERE, TimeDelta::FromMilliseconds(start_delay_ms_), this, &SmoothedThrobber::StartDelayOver); } @@ -122,7 +122,7 @@ } void SmoothedThrobber::Stop() { - if (!running_) + if (!running()) start_timer_.Stop(); stop_timer_.Stop(); @@ -134,35 +134,87 @@ Throbber::Stop(); } -// Checkmark throbber --------------------------------------------------------- +// Material throbber ----------------------------------------------------------- -CheckmarkThrobber::CheckmarkThrobber() - : Throbber(kFrameTimeMs, false), - checked_(false), - checkmark_(ui::ResourceBundle::GetSharedInstance().GetImageNamed( - IDR_CHECKMARK).ToImageSkia()) { +// The length of a frame in milliseconds. +// TODO(estade): remove the +10 when the -10 is removed from Throbber::Start(). +static const int kMaterialThrobberFrameTimeMs = 30 + 10; + +MaterialThrobber::MaterialThrobber() : + Throbber(kMaterialThrobberFrameTimeMs, false), + preferred_diameter_(0), + checked_(false), + checkmark_(nullptr) { } -void CheckmarkThrobber::SetChecked(bool checked) { - bool changed = checked != checked_; - if (changed) { - checked_ = checked; - SchedulePaint(); - } +MaterialThrobber::~MaterialThrobber() { } -void CheckmarkThrobber::OnPaint(gfx::Canvas* canvas) { - if (running_) { - // Let the throbber throb... - Throbber::OnPaint(canvas); +void MaterialThrobber::SetChecked(bool checked) { + if (checked == checked_) + return; + + checked_ = checked; + SchedulePaint(); +} + +gfx::Size MaterialThrobber::GetPreferredSize() const { + if (preferred_diameter_ == 0) + return Throbber::GetPreferredSize(); + + return gfx::Size(preferred_diameter_, preferred_diameter_); +} + +int MaterialThrobber::GetHeightForWidth(int w) const { + return w; +} + +void MaterialThrobber::OnPaint(gfx::Canvas* canvas) { + if (!running()) { + if (checked_) { + if (!checkmark_) { + checkmark_ = ui::ResourceBundle::GetSharedInstance().GetImageNamed( + IDR_CHECKMARK).ToImageSkia(); + } + + int checkmark_x = (width() - checkmark_->width()) / 2; + int checkmark_y = (height() - checkmark_->height()) / 2; + canvas->DrawImageInt(*checkmark_, checkmark_x, checkmark_y); + } return; } - // Otherwise we paint our tick mark or nothing depending on our state. - if (checked_) { - int checkmark_x = (width() - checkmark_->width()) / 2; - int checkmark_y = (height() - checkmark_->height()) / 2; - canvas->DrawImageInt(*checkmark_, checkmark_x, checkmark_y); - } + + gfx::Rect bounds = GetContentsBounds(); + // Inset by half the stroke width to make sure the whole arc is inside + // the visible rect. + SkScalar stroke_width = SkIntToScalar(bounds.width()) / 10.0; + gfx::Rect oval = bounds; + int inset = SkScalarCeilToInt(stroke_width / 2.0); + oval.Inset(inset, inset); + + // Calculate start and end points. The angles are counter-clockwise because + // the throbber spins counter-clockwise. The finish angle starts at 12 o'clock + // (90 degrees) and rotates steadily. The start angle trails 180 degrees + // behind, except for the first half revolution, when it stays at 12 o'clock. + base::TimeDelta revolution_time = base::TimeDelta::FromMilliseconds(1320); + base::TimeDelta elapsed_time = base::Time::Now() - start_time(); + int64_t twelve_oclock = 90; + int64_t finish_angle = twelve_oclock + 360 * elapsed_time / revolution_time; + int64_t start_angle = std::max(finish_angle - 180, twelve_oclock); + + SkPath path; + // Negate the angles to convert to the clockwise numbers Skia expects. + path.arcTo(gfx::RectToSkRect(oval), -start_angle, + -(finish_angle - start_angle), true); + + SkPaint paint; + // TODO(estade): find a place for this color. + paint.setColor(SkColorSetRGB(0x42, 0x85, 0xF4)); + paint.setStrokeCap(SkPaint::kRound_Cap); + paint.setStrokeWidth(stroke_width); + paint.setStyle(SkPaint::kStroke_Style); + paint.setAntiAlias(true); + canvas->DrawPath(path, paint); } } // namespace views
diff --git a/ui/views/controls/throbber.h b/ui/views/controls/throbber.h index 0ee428d..73eedb9 100644 --- a/ui/views/controls/throbber.h +++ b/ui/views/controls/throbber.h
@@ -41,12 +41,15 @@ void OnPaint(gfx::Canvas* canvas) override; protected: + bool running() const { return running_; } + base::Time start_time() const { return start_time_; } + // Specifies whether the throbber is currently animating or not - bool running_; private: void Run(); + bool running_; bool paint_while_stopped_; int frame_count_; // How many frames we have. base::Time start_time_; // Time when Start was called. @@ -94,32 +97,37 @@ DISALLOW_COPY_AND_ASSIGN(SmoothedThrobber); }; -// A CheckmarkThrobber is a special variant of throbber that has three states: -// 1. not yet completed (which paints nothing) -// 2. working (which paints the throbber animation) -// 3. completed (which paints a checkmark) -// -class VIEWS_EXPORT CheckmarkThrobber : public Throbber { +// A throbber that follows material design guidelines. This is a specialization +// of Throbber for now, but should probably be folded into/replace Throbber +// eventually. +class VIEWS_EXPORT MaterialThrobber : public Throbber { public: - CheckmarkThrobber(); + MaterialThrobber(); + ~MaterialThrobber() override; - // If checked is true, the throbber stops spinning and displays a checkmark. - // If checked is false, the throbber stops spinning and displays nothing. + // Stop spinning and, if checked is true, display a checkmark. void SetChecked(bool checked); - // Overridden from Throbber: + void set_preferred_diameter(int diameter) { preferred_diameter_ = diameter; } + + // View implementation. + gfx::Size GetPreferredSize() const override; + int GetHeightForWidth(int w) const override; void OnPaint(gfx::Canvas* canvas) override; private: - static const int kFrameTimeMs = 30; + // The preferred width and height for this view. Zero indicates the size is + // set by the parent class (i.e. matches the size of the pre-material + // sprites). + int preferred_diameter_; // Whether or not we should display a checkmark. bool checked_; - // The checkmark image. + // The checkmark image. Will be null until it's used (if ever). const gfx::ImageSkia* checkmark_; - DISALLOW_COPY_AND_ASSIGN(CheckmarkThrobber); + DISALLOW_COPY_AND_ASSIGN(MaterialThrobber); }; } // namespace views
diff --git a/ui/views/controls/webview/web_dialog_view.cc b/ui/views/controls/webview/web_dialog_view.cc index 8fe27bf0..4a5a114 100644 --- a/ui/views/controls/webview/web_dialog_view.cc +++ b/ui/views/controls/webview/web_dialog_view.cc
@@ -247,6 +247,7 @@ void WebDialogView::OnCloseContents(WebContents* source, bool* out_close_dialog) { + DCHECK(out_close_dialog); if (delegate_) delegate_->OnCloseContents(source, out_close_dialog); }
diff --git a/ui/views/widget/desktop_aura/desktop_drag_drop_client_win.h b/ui/views/widget/desktop_aura/desktop_drag_drop_client_win.h index c46e242..4cc0e8a8 100644 --- a/ui/views/widget/desktop_aura/desktop_drag_drop_client_win.h +++ b/ui/views/widget/desktop_aura/desktop_drag_drop_client_win.h
@@ -24,22 +24,19 @@ : public aura::client::DragDropClient { public: DesktopDragDropClientWin(aura::Window* root_window, HWND window); - virtual ~DesktopDragDropClientWin(); + ~DesktopDragDropClientWin() override; // Overridden from aura::client::DragDropClient: - virtual int StartDragAndDrop( - const ui::OSExchangeData& data, - aura::Window* root_window, - aura::Window* source_window, - const gfx::Point& screen_location, - int operation, - ui::DragDropTypes::DragEventSource source) override; - virtual void DragUpdate(aura::Window* target, - const ui::LocatedEvent& event) override; - virtual void Drop(aura::Window* target, - const ui::LocatedEvent& event) override; - virtual void DragCancel() override; - virtual bool IsDragDropInProgress() override; + int StartDragAndDrop(const ui::OSExchangeData& data, + aura::Window* root_window, + aura::Window* source_window, + const gfx::Point& screen_location, + int operation, + ui::DragDropTypes::DragEventSource source) override; + void DragUpdate(aura::Window* target, const ui::LocatedEvent& event) override; + void Drop(aura::Window* target, const ui::LocatedEvent& event) override; + void DragCancel() override; + bool IsDragDropInProgress() override; void OnNativeWidgetDestroying(HWND window);
diff --git a/ui/views/widget/desktop_aura/desktop_drop_target_win.h b/ui/views/widget/desktop_aura/desktop_drop_target_win.h index d376182..73dc15f 100644 --- a/ui/views/widget/desktop_aura/desktop_drop_target_win.h +++ b/ui/views/widget/desktop_aura/desktop_drop_target_win.h
@@ -29,26 +29,26 @@ public aura::WindowObserver { public: DesktopDropTargetWin(aura::Window* root_window, HWND window); - virtual ~DesktopDropTargetWin(); + ~DesktopDropTargetWin() override; private: // ui::DropTargetWin implementation: - virtual DWORD OnDragEnter(IDataObject* data_object, - DWORD key_state, - POINT position, - DWORD effect) override; - virtual DWORD OnDragOver(IDataObject* data_object, - DWORD key_state, - POINT position, - DWORD effect) override; - virtual void OnDragLeave(IDataObject* data_object) override; - virtual DWORD OnDrop(IDataObject* data_object, - DWORD key_state, - POINT position, - DWORD effect) override; + DWORD OnDragEnter(IDataObject* data_object, + DWORD key_state, + POINT position, + DWORD effect) override; + DWORD OnDragOver(IDataObject* data_object, + DWORD key_state, + POINT position, + DWORD effect) override; + void OnDragLeave(IDataObject* data_object) override; + DWORD OnDrop(IDataObject* data_object, + DWORD key_state, + POINT position, + DWORD effect) override; // aura::WindowObserver implementation: - virtual void OnWindowDestroyed(aura::Window* window) override; + void OnWindowDestroyed(aura::Window* window) override; // Common functionality for the ui::DropTargetWin methods to translate from // COM data types to Aura ones.
diff --git a/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc b/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc index 8472b6a..c82ced73 100644 --- a/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc +++ b/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc
@@ -15,6 +15,7 @@ #include "ui/aura/window_property.h" #include "ui/aura/window_tree_host.h" #include "ui/base/hit_test.h" +#include "ui/base/ime/input_method.h" #include "ui/base/ui_base_switches_util.h" #include "ui/compositor/layer.h" #include "ui/gfx/canvas.h" @@ -380,6 +381,9 @@ view_for_activation = GetWidget()->GetRootView(); activation_client->ActivateWindow( view_for_activation->GetWidget()->GetNativeView()); + // Refreshes the focus info to IMF in case that IMF cached the old info + // about focused text input client when it was "inactive". + GetHostInputMethod()->OnFocus(); } } else { // If we're not active we need to deactivate the corresponding @@ -387,8 +391,10 @@ // deactivated (child widgets don't get native desktop activation changes, // only aura activation changes). aura::Window* active_window = activation_client->GetActiveWindow(); - if (active_window) + if (active_window) { activation_client->DeactivateWindow(active_window); + GetHostInputMethod()->OnBlur(); + } } }
diff --git a/ui/views/widget/desktop_aura/desktop_screen_win.h b/ui/views/widget/desktop_aura/desktop_screen_win.h index 59776a6..16357a3 100644 --- a/ui/views/widget/desktop_aura/desktop_screen_win.h +++ b/ui/views/widget/desktop_aura/desktop_screen_win.h
@@ -13,14 +13,13 @@ class VIEWS_EXPORT DesktopScreenWin : public gfx::ScreenWin { public: DesktopScreenWin(); - virtual ~DesktopScreenWin(); + ~DesktopScreenWin() override; private: // Overridden from gfx::ScreenWin: - virtual gfx::Display GetDisplayMatching( - const gfx::Rect& match_rect) const override; - virtual HWND GetHWNDFromNativeView(gfx::NativeView window) const override; - virtual gfx::NativeWindow GetNativeWindowFromHWND(HWND hwnd) const override; + gfx::Display GetDisplayMatching(const gfx::Rect& match_rect) const override; + HWND GetHWNDFromNativeView(gfx::NativeView window) const override; + gfx::NativeWindow GetNativeWindowFromHWND(HWND hwnd) const override; DISALLOW_COPY_AND_ASSIGN(DesktopScreenWin); };
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_win.h b/ui/views/widget/desktop_aura/desktop_window_tree_host_win.h index 8e8aec2..6e32fc7 100644 --- a/ui/views/widget/desktop_aura/desktop_window_tree_host_win.h +++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_win.h
@@ -38,170 +38,165 @@ DesktopWindowTreeHostWin( internal::NativeWidgetDelegate* native_widget_delegate, DesktopNativeWidgetAura* desktop_native_widget_aura); - virtual ~DesktopWindowTreeHostWin(); + ~DesktopWindowTreeHostWin() override; // A way of converting an HWND into a content window. static aura::Window* GetContentWindowForHWND(HWND hwnd); protected: // Overridden from DesktopWindowTreeHost: - virtual void Init(aura::Window* content_window, - const Widget::InitParams& params) override; - virtual void OnNativeWidgetCreated(const Widget::InitParams& params) override; - virtual scoped_ptr<corewm::Tooltip> CreateTooltip() override; - virtual scoped_ptr<aura::client::DragDropClient> - CreateDragDropClient(DesktopNativeCursorManager* cursor_manager) override; - virtual void Close() override; - virtual void CloseNow() override; - virtual aura::WindowTreeHost* AsWindowTreeHost() override; - virtual void ShowWindowWithState(ui::WindowShowState show_state) override; - virtual void ShowMaximizedWithBounds( - const gfx::Rect& restored_bounds) override; - virtual bool IsVisible() const override; - virtual void SetSize(const gfx::Size& size) override; - virtual void StackAtTop() override; - virtual void CenterWindow(const gfx::Size& size) override; - virtual void GetWindowPlacement( - gfx::Rect* bounds, - ui::WindowShowState* show_state) const override; - virtual gfx::Rect GetWindowBoundsInScreen() const override; - virtual gfx::Rect GetClientAreaBoundsInScreen() const override; - virtual gfx::Rect GetRestoredBounds() const override; - virtual gfx::Rect GetWorkAreaBoundsInScreen() const override; - virtual void SetShape(gfx::NativeRegion native_region) override; - virtual void Activate() override; - virtual void Deactivate() override; - virtual bool IsActive() const override; - virtual void Maximize() override; - virtual void Minimize() override; - virtual void Restore() override; - virtual bool IsMaximized() const override; - virtual bool IsMinimized() const override; - virtual bool HasCapture() const override; - virtual void SetAlwaysOnTop(bool always_on_top) override; - virtual bool IsAlwaysOnTop() const override; - virtual void SetVisibleOnAllWorkspaces(bool always_visible) override; - virtual bool SetWindowTitle(const base::string16& title) override; - virtual void ClearNativeFocus() override; - virtual Widget::MoveLoopResult RunMoveLoop( + void Init(aura::Window* content_window, + const Widget::InitParams& params) override; + void OnNativeWidgetCreated(const Widget::InitParams& params) override; + scoped_ptr<corewm::Tooltip> CreateTooltip() override; + scoped_ptr<aura::client::DragDropClient> CreateDragDropClient( + DesktopNativeCursorManager* cursor_manager) override; + void Close() override; + void CloseNow() override; + aura::WindowTreeHost* AsWindowTreeHost() override; + void ShowWindowWithState(ui::WindowShowState show_state) override; + void ShowMaximizedWithBounds(const gfx::Rect& restored_bounds) override; + bool IsVisible() const override; + void SetSize(const gfx::Size& size) override; + void StackAtTop() override; + void CenterWindow(const gfx::Size& size) override; + void GetWindowPlacement(gfx::Rect* bounds, + ui::WindowShowState* show_state) const override; + gfx::Rect GetWindowBoundsInScreen() const override; + gfx::Rect GetClientAreaBoundsInScreen() const override; + gfx::Rect GetRestoredBounds() const override; + gfx::Rect GetWorkAreaBoundsInScreen() const override; + void SetShape(gfx::NativeRegion native_region) override; + void Activate() override; + void Deactivate() override; + bool IsActive() const override; + void Maximize() override; + void Minimize() override; + void Restore() override; + bool IsMaximized() const override; + bool IsMinimized() const override; + bool HasCapture() const override; + void SetAlwaysOnTop(bool always_on_top) override; + bool IsAlwaysOnTop() const override; + void SetVisibleOnAllWorkspaces(bool always_visible) override; + bool SetWindowTitle(const base::string16& title) override; + void ClearNativeFocus() override; + Widget::MoveLoopResult RunMoveLoop( const gfx::Vector2d& drag_offset, Widget::MoveLoopSource source, Widget::MoveLoopEscapeBehavior escape_behavior) override; - virtual void EndMoveLoop() override; - virtual void SetVisibilityChangedAnimationsEnabled(bool value) override; - virtual bool ShouldUseNativeFrame() const override; - virtual bool ShouldWindowContentsBeTransparent() const override; - virtual void FrameTypeChanged() override; - virtual void SetFullscreen(bool fullscreen) override; - virtual bool IsFullscreen() const override; - virtual void SetOpacity(unsigned char opacity) override; - virtual void SetWindowIcons(const gfx::ImageSkia& window_icon, - const gfx::ImageSkia& app_icon) override; - virtual void InitModalType(ui::ModalType modal_type) override; - virtual void FlashFrame(bool flash_frame) override; - virtual void OnRootViewLayout() override; - virtual void OnNativeWidgetFocus() override; - virtual void OnNativeWidgetBlur() override; - virtual bool IsAnimatingClosed() const override; - virtual bool IsTranslucentWindowOpacitySupported() const override; - virtual void SizeConstraintsChanged() override; + void EndMoveLoop() override; + void SetVisibilityChangedAnimationsEnabled(bool value) override; + bool ShouldUseNativeFrame() const override; + bool ShouldWindowContentsBeTransparent() const override; + void FrameTypeChanged() override; + void SetFullscreen(bool fullscreen) override; + bool IsFullscreen() const override; + void SetOpacity(unsigned char opacity) override; + void SetWindowIcons(const gfx::ImageSkia& window_icon, + const gfx::ImageSkia& app_icon) override; + void InitModalType(ui::ModalType modal_type) override; + void FlashFrame(bool flash_frame) override; + void OnRootViewLayout() override; + void OnNativeWidgetFocus() override; + void OnNativeWidgetBlur() override; + bool IsAnimatingClosed() const override; + bool IsTranslucentWindowOpacitySupported() const override; + void SizeConstraintsChanged() override; // Overridden from aura::WindowTreeHost: - virtual ui::EventSource* GetEventSource() override; - virtual gfx::AcceleratedWidget GetAcceleratedWidget() override; - virtual void Show() override; - virtual void Hide() override; - virtual gfx::Rect GetBounds() const override; - virtual void SetBounds(const gfx::Rect& bounds) override; - virtual gfx::Point GetLocationOnNativeScreen() const override; - virtual void SetCapture() override; - virtual void ReleaseCapture() override; - virtual void SetCursorNative(gfx::NativeCursor cursor) override; - virtual void OnCursorVisibilityChangedNative(bool show) override; - virtual void MoveCursorToNative(const gfx::Point& location) override; + ui::EventSource* GetEventSource() override; + gfx::AcceleratedWidget GetAcceleratedWidget() override; + void Show() override; + void Hide() override; + gfx::Rect GetBounds() const override; + void SetBounds(const gfx::Rect& bounds) override; + gfx::Point GetLocationOnNativeScreen() const override; + void SetCapture() override; + void ReleaseCapture() override; + void SetCursorNative(gfx::NativeCursor cursor) override; + void OnCursorVisibilityChangedNative(bool show) override; + void MoveCursorToNative(const gfx::Point& location) override; // Overridden frm ui::EventSource - virtual ui::EventProcessor* GetEventProcessor() override; + ui::EventProcessor* GetEventProcessor() override; // Overridden from aura::client::AnimationHost - virtual void SetHostTransitionOffsets( + void SetHostTransitionOffsets( const gfx::Vector2d& top_left_delta, const gfx::Vector2d& bottom_right_delta) override; - virtual void OnWindowHidingAnimationCompleted() override; + void OnWindowHidingAnimationCompleted() override; // Overridden from HWNDMessageHandlerDelegate: - virtual bool IsWidgetWindow() const override; - virtual bool IsUsingCustomFrame() const override; - virtual void SchedulePaint() override; - virtual void EnableInactiveRendering() override; - virtual bool IsInactiveRenderingDisabled() override; - virtual bool CanResize() const override; - virtual bool CanMaximize() const override; - virtual bool CanMinimize() const override; - virtual bool CanActivate() const override; - virtual bool WidgetSizeIsClientSize() const override; - virtual bool IsModal() const override; - virtual int GetInitialShowState() const override; - virtual bool WillProcessWorkAreaChange() const override; - virtual int GetNonClientComponent(const gfx::Point& point) const override; - virtual void GetWindowMask(const gfx::Size& size, gfx::Path* path) override; - virtual bool GetClientAreaInsets(gfx::Insets* insets) const override; - virtual void GetMinMaxSize(gfx::Size* min_size, - gfx::Size* max_size) const override; - virtual gfx::Size GetRootViewSize() const override; - virtual void ResetWindowControls() override; - virtual void PaintLayeredWindow(gfx::Canvas* canvas) override; - virtual gfx::NativeViewAccessible GetNativeViewAccessible() override; - virtual bool ShouldHandleSystemCommands() const override; - virtual InputMethod* GetInputMethod() override; - virtual void HandleAppDeactivated() override; - virtual void HandleActivationChanged(bool active) override; - virtual bool HandleAppCommand(short command) override; - virtual void HandleCancelMode() override; - virtual void HandleCaptureLost() override; - virtual void HandleClose() override; - virtual bool HandleCommand(int command) override; - virtual void HandleAccelerator(const ui::Accelerator& accelerator) override; - virtual void HandleCreate() override; - virtual void HandleDestroying() override; - virtual void HandleDestroyed() override; - virtual bool HandleInitialFocus(ui::WindowShowState show_state) override; - virtual void HandleDisplayChange() override; - virtual void HandleBeginWMSizeMove() override; - virtual void HandleEndWMSizeMove() override; - virtual void HandleMove() override; - virtual void HandleWorkAreaChanged() override; - virtual void HandleVisibilityChanging(bool visible) override; - virtual void HandleVisibilityChanged(bool visible) override; - virtual void HandleClientSizeChanged(const gfx::Size& new_size) override; - virtual void HandleFrameChanged() override; - virtual void HandleNativeFocus(HWND last_focused_window) override; - virtual void HandleNativeBlur(HWND focused_window) override; - virtual bool HandleMouseEvent(const ui::MouseEvent& event) override; - virtual bool HandleKeyEvent(const ui::KeyEvent& event) override; - virtual bool HandleUntranslatedKeyEvent(const ui::KeyEvent& event) override; - virtual void HandleTouchEvent(const ui::TouchEvent& event) override; - virtual bool HandleIMEMessage(UINT message, - WPARAM w_param, - LPARAM l_param, - LRESULT* result) override; - virtual void HandleInputLanguageChange(DWORD character_set, - HKL input_language_id) override; - virtual bool HandlePaintAccelerated(const gfx::Rect& invalid_rect) override; - virtual void HandlePaint(gfx::Canvas* canvas) override; - virtual bool HandleTooltipNotify(int w_param, - NMHDR* l_param, - LRESULT* l_result) override; - virtual void HandleMenuLoop(bool in_menu_loop) override; - virtual bool PreHandleMSG(UINT message, - WPARAM w_param, - LPARAM l_param, - LRESULT* result) override; - virtual void PostHandleMSG(UINT message, - WPARAM w_param, - LPARAM l_param) override; - virtual bool HandleScrollEvent(const ui::ScrollEvent& event) override; - virtual void HandleWindowSizeChanging() override; + bool IsWidgetWindow() const override; + bool IsUsingCustomFrame() const override; + void SchedulePaint() override; + void EnableInactiveRendering() override; + bool IsInactiveRenderingDisabled() override; + bool CanResize() const override; + bool CanMaximize() const override; + bool CanMinimize() const override; + bool CanActivate() const override; + bool WidgetSizeIsClientSize() const override; + bool IsModal() const override; + int GetInitialShowState() const override; + bool WillProcessWorkAreaChange() const override; + int GetNonClientComponent(const gfx::Point& point) const override; + void GetWindowMask(const gfx::Size& size, gfx::Path* path) override; + bool GetClientAreaInsets(gfx::Insets* insets) const override; + void GetMinMaxSize(gfx::Size* min_size, gfx::Size* max_size) const override; + gfx::Size GetRootViewSize() const override; + void ResetWindowControls() override; + void PaintLayeredWindow(gfx::Canvas* canvas) override; + gfx::NativeViewAccessible GetNativeViewAccessible() override; + bool ShouldHandleSystemCommands() const override; + InputMethod* GetInputMethod() override; + void HandleAppDeactivated() override; + void HandleActivationChanged(bool active) override; + bool HandleAppCommand(short command) override; + void HandleCancelMode() override; + void HandleCaptureLost() override; + void HandleClose() override; + bool HandleCommand(int command) override; + void HandleAccelerator(const ui::Accelerator& accelerator) override; + void HandleCreate() override; + void HandleDestroying() override; + void HandleDestroyed() override; + bool HandleInitialFocus(ui::WindowShowState show_state) override; + void HandleDisplayChange() override; + void HandleBeginWMSizeMove() override; + void HandleEndWMSizeMove() override; + void HandleMove() override; + void HandleWorkAreaChanged() override; + void HandleVisibilityChanging(bool visible) override; + void HandleVisibilityChanged(bool visible) override; + void HandleClientSizeChanged(const gfx::Size& new_size) override; + void HandleFrameChanged() override; + void HandleNativeFocus(HWND last_focused_window) override; + void HandleNativeBlur(HWND focused_window) override; + bool HandleMouseEvent(const ui::MouseEvent& event) override; + bool HandleKeyEvent(const ui::KeyEvent& event) override; + bool HandleUntranslatedKeyEvent(const ui::KeyEvent& event) override; + void HandleTouchEvent(const ui::TouchEvent& event) override; + bool HandleIMEMessage(UINT message, + WPARAM w_param, + LPARAM l_param, + LRESULT* result) override; + void HandleInputLanguageChange(DWORD character_set, + HKL input_language_id) override; + bool HandlePaintAccelerated(const gfx::Rect& invalid_rect) override; + void HandlePaint(gfx::Canvas* canvas) override; + bool HandleTooltipNotify(int w_param, + NMHDR* l_param, + LRESULT* l_result) override; + void HandleMenuLoop(bool in_menu_loop) override; + bool PreHandleMSG(UINT message, + WPARAM w_param, + LPARAM l_param, + LRESULT* result) override; + void PostHandleMSG(UINT message, WPARAM w_param, LPARAM l_param) override; + bool HandleScrollEvent(const ui::ScrollEvent& event) override; + void HandleWindowSizeChanging() override; Widget* GetWidget(); const Widget* GetWidget() const;
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_x11_interactive_uitest.cc b/ui/views/widget/desktop_aura/desktop_window_tree_host_x11_interactive_uitest.cc index fb76c9b..3876873d 100644 --- a/ui/views/widget/desktop_aura/desktop_window_tree_host_x11_interactive_uitest.cc +++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_x11_interactive_uitest.cc
@@ -23,6 +23,8 @@ #include "ui/gfx/geometry/rect.h" #include "ui/gfx/x/x11_atom_cache.h" #include "ui/gl/gl_surface.h" +#include "ui/views/controls/textfield/textfield.h" +#include "ui/views/ime/input_method.h" #include "ui/views/test/views_test_base.h" #include "ui/views/test/x11_property_change_waiter.h" #include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h" @@ -251,4 +253,29 @@ window2->RemovePreTargetHandler(&recorder2); } +TEST_F(DesktopWindowTreeHostX11Test, InputMethodFocus) { + scoped_ptr<Widget> widget(CreateWidget(gfx::Rect(100, 100, 100, 100))); + scoped_ptr<Textfield> textfield(new Textfield); + textfield->SetBounds(0, 0, 200, 20); + widget->GetRootView()->AddChildView(textfield.get()); + widget->Show(); + textfield->RequestFocus(); + + EXPECT_FALSE(widget->IsActive()); + EXPECT_EQ(ui::TEXT_INPUT_TYPE_NONE, + widget->GetInputMethod()->GetTextInputType()); + + widget->Activate(); + + EXPECT_TRUE(widget->IsActive()); + EXPECT_EQ(ui::TEXT_INPUT_TYPE_TEXT, + widget->GetInputMethod()->GetTextInputType()); + + widget->Deactivate(); + + EXPECT_FALSE(widget->IsActive()); + EXPECT_EQ(ui::TEXT_INPUT_TYPE_NONE, + widget->GetInputMethod()->GetTextInputType()); +} + } // namespace views
diff --git a/ui/views/widget/desktop_aura/x11_desktop_handler.cc b/ui/views/widget/desktop_aura/x11_desktop_handler.cc index dc3c8acc..33b6347 100644 --- a/ui/views/widget/desktop_aura/x11_desktop_handler.cc +++ b/ui/views/widget/desktop_aura/x11_desktop_handler.cc
@@ -75,7 +75,7 @@ } void X11DesktopHandler::ActivateWindow(::Window window) { - if (current_window_ == window && + if ((current_window_ == None || current_window_ == window) && current_window_active_state_ == NOT_ACTIVE) { // |window| is most likely still active wrt to the X server. Undo the // changes made in DeactivateWindow().
diff --git a/ui/views/win/hwnd_message_handler.h b/ui/views/win/hwnd_message_handler.h index b7db7f2..a966eae2 100644 --- a/ui/views/win/hwnd_message_handler.h +++ b/ui/views/win/hwnd_message_handler.h
@@ -115,7 +115,7 @@ public ui::WindowEventTarget { public: explicit HWNDMessageHandler(HWNDMessageHandlerDelegate* delegate); - ~HWNDMessageHandler(); + ~HWNDMessageHandler() override; void Init(HWND parent, const gfx::Rect& bounds); void InitModalType(ui::ModalType modal_type); @@ -212,38 +212,36 @@ typedef std::set<DWORD> TouchIDs; // Overridden from internal::InputMethodDelegate: - virtual void DispatchKeyEventPostIME(const ui::KeyEvent& key) override; + void DispatchKeyEventPostIME(const ui::KeyEvent& key) override; // Overridden from WindowImpl: - virtual HICON GetDefaultWindowIcon() const override; - virtual HICON GetSmallWindowIcon() const override; - virtual LRESULT OnWndProc(UINT message, - WPARAM w_param, - LPARAM l_param) override; + HICON GetDefaultWindowIcon() const override; + HICON GetSmallWindowIcon() const override; + LRESULT OnWndProc(UINT message, WPARAM w_param, LPARAM l_param) override; // Overridden from WindowEventTarget - virtual LRESULT HandleMouseMessage(unsigned int message, - WPARAM w_param, - LPARAM l_param, - bool* handled) override; - virtual LRESULT HandleKeyboardMessage(unsigned int message, - WPARAM w_param, - LPARAM l_param, - bool* handled) override; - virtual LRESULT HandleTouchMessage(unsigned int message, - WPARAM w_param, - LPARAM l_param, - bool* handled) override; + LRESULT HandleMouseMessage(unsigned int message, + WPARAM w_param, + LPARAM l_param, + bool* handled) override; + LRESULT HandleKeyboardMessage(unsigned int message, + WPARAM w_param, + LPARAM l_param, + bool* handled) override; + LRESULT HandleTouchMessage(unsigned int message, + WPARAM w_param, + LPARAM l_param, + bool* handled) override; - virtual LRESULT HandleScrollMessage(unsigned int message, - WPARAM w_param, - LPARAM l_param, - bool* handled) override; + LRESULT HandleScrollMessage(unsigned int message, + WPARAM w_param, + LPARAM l_param, + bool* handled) override; - virtual LRESULT HandleNcHitTestMessage(unsigned int message, - WPARAM w_param, - LPARAM l_param, - bool* handled) override; + LRESULT HandleNcHitTestMessage(unsigned int message, + WPARAM w_param, + LPARAM l_param, + bool* handled) override; // Returns the auto-hide edges of the appbar. See // ViewsDelegate::GetAppbarAutohideEdges() for details. If the edges change,
diff --git a/ui/web_dialogs/test/test_web_dialog_delegate.cc b/ui/web_dialogs/test/test_web_dialog_delegate.cc index e35eecf..41b07bc6 100644 --- a/ui/web_dialogs/test/test_web_dialog_delegate.cc +++ b/ui/web_dialogs/test/test_web_dialog_delegate.cc
@@ -60,9 +60,8 @@ } void TestWebDialogDelegate::OnCloseContents(WebContents* source, - bool* out_close_dialog) { - if (out_close_dialog) - *out_close_dialog = true; + bool* out_close_dialog) { + *out_close_dialog = true; } bool TestWebDialogDelegate::ShouldShowDialogTitle() const {
diff --git a/ui/web_dialogs/web_dialog_delegate.h b/ui/web_dialogs/web_dialog_delegate.h index 6d2079d..640dfd0 100644 --- a/ui/web_dialogs/web_dialog_delegate.h +++ b/ui/web_dialogs/web_dialog_delegate.h
@@ -98,6 +98,7 @@ // away. Only relevant if your dialog hosts code that calls // windows.close() and you've allowed that. If the output parameter // is set to true, then the dialog is closed. The default is false. + // |out_close_dialog| is never NULL. virtual void OnCloseContents(content::WebContents* source, bool* out_close_dialog) = 0;
diff --git a/ui/webui/resources/js/cr/ui/list.js b/ui/webui/resources/js/cr/ui/list.js index e3f152a..e89713b 100644 --- a/ui/webui/resources/js/cr/ui/list.js +++ b/ui/webui/resources/js/cr/ui/list.js
@@ -815,7 +815,7 @@ /** * Creates a new list item. - * @param {*} value The value to use for the item. + * @param {?} value The value to use for the item. * @return {!cr.ui.ListItem} The newly created list item. */ createItem: function(value) {
diff --git a/ui/webui/resources/js/cr/ui/menu_button.js b/ui/webui/resources/js/cr/ui/menu_button.js index e240a32..e7f9804 100644 --- a/ui/webui/resources/js/cr/ui/menu_button.js +++ b/ui/webui/resources/js/cr/ui/menu_button.js
@@ -44,6 +44,7 @@ decorate: function() { this.addEventListener('mousedown', this); this.addEventListener('keydown', this); + this.addEventListener('dblclick', this); // Adding the 'custom-appearance' class prevents widgets.css from changing // the appearance of this element. @@ -154,6 +155,19 @@ case 'resize': this.hideMenu(); break; + case 'contextmenu': + if ((!this.menu || !this.menu.contains(e.target)) && + (!this.hideTimestamp_ || Date.now() - this.hideTimestamp_ > 50)) + this.showMenu(true); + e.preventDefault(); + // Don't allow elements further up in the DOM to show their menus. + e.stopPropagation(); + break; + case 'dblclick': + // Don't allow double click events to propagate. + e.preventDefault(); + e.stopPropagation(); + break; } }, @@ -186,6 +200,7 @@ this.showingEvents_.add(doc, 'scroll', this, true); this.showingEvents_.add(win, 'popstate', this); this.showingEvents_.add(win, 'resize', this); + this.showingEvents_.add(this.menu, 'contextmenu', this); this.showingEvents_.add(this.menu, 'activate', this); this.positionMenu_(); @@ -212,6 +227,11 @@ this.showingEvents_.removeAll(); this.focus(); + + // On windows we might hide the menu in a right mouse button up and if + // that is the case we wait some short period before we allow the menu + // to be shown again. + this.hideTimestamp_ = cr.isWindows ? Date.now() : 0; }, /**
diff --git a/ui/wm/core/nested_accelerator_dispatcher_win.cc b/ui/wm/core/nested_accelerator_dispatcher_win.cc index f3fc5d78..8fa42fef 100644 --- a/ui/wm/core/nested_accelerator_dispatcher_win.cc +++ b/ui/wm/core/nested_accelerator_dispatcher_win.cc
@@ -31,16 +31,16 @@ NestedAcceleratorDispatcherWin(NestedAcceleratorDelegate* delegate, MessagePumpDispatcher* nested) : NestedAcceleratorDispatcher(delegate), nested_dispatcher_(nested) {} - virtual ~NestedAcceleratorDispatcherWin() {} + ~NestedAcceleratorDispatcherWin() override {} private: // NestedAcceleratorDispatcher: - virtual scoped_ptr<base::RunLoop> CreateRunLoop() override { + scoped_ptr<base::RunLoop> CreateRunLoop() override { return make_scoped_ptr(new base::RunLoop(this)); } // MessagePumpDispatcher: - virtual uint32_t Dispatch(const MSG& event) override { + uint32_t Dispatch(const MSG& event) override { if (IsKeyEvent(event)) { ui::KeyEvent key_event(event); ui::Accelerator accelerator = CreateAcceleratorFromKeyEvent(key_event);
diff --git a/url/gurl.h b/url/gurl.h index 5e804a1..b9136b8 100644 --- a/url/gurl.h +++ b/url/gurl.h
@@ -225,12 +225,13 @@ // Returns true if the scheme indicates a secure connection. // - // NOTE: This function is deprecated. You probably want |SchemeUsesTLS| (if - // you just want to know if a scheme uses TLS for network transport) or - // Chromium's |IsOriginSecure| for a higher-level test about an origin's - // security. See those functions' documentation for more detail. + // NOTE: This function is deprecated. You probably want + // |SchemeIsCryptographic| (if you just want to know if a scheme uses TLS for + // network transport) or Chromium's |IsOriginSecure| for a higher-level test + // about an origin's security. See those functions' documentation for more + // detail. // - // TODO(palmer): Audit callers and change them to |SchemeUsesTLS| or + // TODO(palmer): Audit callers and change them to |SchemeIsCryptographic| or // |IsOriginSecure|, as appropriate. Then remove |SchemeIsSecure|. // crbug.com/362214 bool SchemeIsSecure() const { @@ -239,14 +240,14 @@ inner_url()->SchemeIsSecure()); } - // Returns true if the scheme indicates a network connection that uses TLS for - // security. + // Returns true if the scheme indicates a network connection that uses TLS or + // some other cryptographic protocol (e.g. QUIC) for security. // // This function is a not a complete test of whether or not an origin's code // is minimally trustworthy. For that, see Chromium's |IsOriginSecure| for a // higher-level and more complete semantics. See that function's documentation // for more detail. - bool SchemeUsesTLS() const { + bool SchemeIsCryptographic() const { return SchemeIs(url::kHttpsScheme) || SchemeIs(url::kWssScheme); }